From 092fcdcf923c2c05acc46079b651f10425ee98b1 Mon Sep 17 00:00:00 2001 From: Benjamin Moster Date: Sun, 20 Oct 2019 00:50:10 +0200 Subject: [PATCH] Basic commit of all Emerge files --- Makefile | 273 +++ README.md | 1491 +++++++++++++- Template-Config.sh | 73 + Template-TypeOfSystem.sh | 17 + build/.gitignore | 4 + compile-config.perl | 54 + config-makefile | 6 + data/csfrd.dat | 144 ++ data/fq.dat | 421 ++++ data/smf.dat | 1146 +++++++++++ data/ssfr.dat | 281 +++ data/wp.dat | 389 ++++ parameterfiles/emerge.param | 111 ++ src/allocate.c | 600 ++++++ src/allvars.c | 115 ++ src/allvars.h | 639 ++++++ src/clustering.c | 1106 +++++++++++ src/fit.c | 2010 +++++++++++++++++++ src/functions.c | 330 ++++ src/galaxies.c | 730 +++++++ src/main.c | 1558 +++++++++++++++ src/output.c | 1600 +++++++++++++++ src/proto.h | 158 ++ src/read_data.c | 628 ++++++ src/read_trees.c | 2789 +++++++++++++++++++++++++++ src/setup.c | 930 +++++++++ src/statistics.c | 1781 +++++++++++++++++ tools/consistent_trees_to_emerge.sh | 21 + tools/convert_CT_to_emerge.c | 441 +++++ trees/.gitignore | 4 + trees/README | 11 + user-guide.pdf | Bin 0 -> 464563 bytes 32 files changed, 19860 insertions(+), 1 deletion(-) create mode 100644 Makefile create mode 100644 Template-Config.sh create mode 100644 Template-TypeOfSystem.sh create mode 100644 build/.gitignore create mode 100644 compile-config.perl create mode 100644 config-makefile create mode 100644 data/csfrd.dat create mode 100644 data/fq.dat create mode 100644 data/smf.dat create mode 100644 data/ssfr.dat create mode 100644 data/wp.dat create mode 100644 parameterfiles/emerge.param create mode 100644 src/allocate.c create mode 100644 src/allvars.c create mode 100644 src/allvars.h create mode 100644 src/clustering.c create mode 100644 src/fit.c create mode 100644 src/functions.c create mode 100644 src/galaxies.c create mode 100644 src/main.c create mode 100644 src/output.c create mode 100644 src/proto.h create mode 100644 src/read_data.c create mode 100644 src/read_trees.c create mode 100644 src/setup.c create mode 100644 src/statistics.c create mode 100755 tools/consistent_trees_to_emerge.sh create mode 100644 tools/convert_CT_to_emerge.c create mode 100644 trees/.gitignore create mode 100644 trees/README create mode 100644 user-guide.pdf diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8314257 --- /dev/null +++ b/Makefile @@ -0,0 +1,273 @@ +####################################################################### +# # +# Makefile for the Empirical ModEl for the foRmation of GalaxiEs # +# # +####################################################################### +# # +# To build the code, do the following: # +# (1) Copy the file "Template-Config.sh" to "Config.sh" # +# (2) Edit "Config.sh" as needed for your application # +# (3) Copy the file "Template-TypeOfSystem.sh" to "TypeOfSystem.sh" # +# (4) Uncomment your system in "TypeOfSystem.sh" # +# (3) Run "make" # +# # +####################################################################### +# # +# New compile-time options should be added to the file # +# "Template-Config.sh" only. Usually, they should be added there in # +# the disabled/default version. New types of systems can be added # +# below and should also be added to "Template-TypeOfSystem.sh" in # +# the disabled version. # +# # +# Neither "Config.sh" nor "TypeOfSystem.sh" should be checked in to # +# the repository. # +# # +####################################################################### +# # +# Note: It is possible to override the default name of the Config.sh # +# file, if desired, as well as the name of the executable. # +# For example: # +# # +# make CONFIG=NewConfig.sh EXEC=emerge_new # +# # +####################################################################### +# +# +# +# +# ---------------------------------------------------------------- +EXEC = emerge +CONFIG = Config.sh +BUILD_DIR = build +SRC_DIR = src +TOOLS_DIR = tools +# ---------------------------------------------------------------- +include TypeOfSystem.sh +# ---------------------------------------------------------------- +MAKEFILES = Makefile config-makefile TypeOfSystem.sh +PERL = /usr/bin/perl +RESULT := $(shell CONFIG=$(CONFIG) PERL=$(PERL) BUILD_DIR=$(BUILD_DIR) make -f config-makefile) +CONFIGVARS := $(shell cat $(BUILD_DIR)/codeoptions.h) +# ---------------------------------------------------------------- +# +# +# +####################################################################### +# Default options # +####################################################################### +CC = mpicc +MPICHLIB = -lmpich +GSLLIB = -lgsl -lgslcblas +MATHLIB = -lm +####################################################################### +# +# +# +####################################################################### +# Define the types of systems here +####################################################################### +# +# MacBook with OS X Mojave, OpenMPI 3.1.1, and GSL 2.5 +# +ifeq ($(TYPEOFSYSTEM),"MacBook") +CC = /usr/local/bin/mpicc +OPTIMIZE = -O3 -g -Wall -Wno-uninitialized -Wformat-overflow=0 +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -fopenmp +OMP_LIBS = -lgomp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = +GSL_LIBS = +MPICHLIB = +HDF5_INCL = +HDF5_LIB = -lhdf5 -lz +endif +# +# --------------------------------------------------------------------- +# +# ODIN @ MPA +# +# module load gsl/2.1 +# module load impi +# module load hdf5-serial +# +ifeq ($(TYPEOFSYSTEM),"Odin") +CC = mpiicc +OPTIMIZE = -O3 -g -m64 +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -openmp +OMP_LIBS = -openmp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = -I$(GSL_HOME)/include/ +GSL_LIBS = -L$(GSL_HOME)/lib/ -Xlinker -R -Xlinker $(GSL_HOME)/lib +MPICHLIB = +HDF5_INCL = -I$(HDF5_HOME)/include +HDF5_LIB = -L$(HDF5_HOME)/lib -Xlinker -R -Xlinker $(HDF5_HOME)/lib -lhdf5 -lz +OPT += -DNOCALLSOFSYSTEM +endif +# +# --------------------------------------------------------------------- +# +# FREYA @ MPA +# +# module load gsl/2.2 +# module load impi +# module load hdf5-serial +# +ifeq ($(TYPEOFSYSTEM),"Freya") +CC = mpiicc +OPTIMIZE = -O3 -g -m64 +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -qopenmp +OMP_LIBS = -qopenmp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = -I$(GSL_HOME)/include/ +GSL_LIBS = -L$(GSL_HOME)/lib/ -Xlinker -R -Xlinker $(GSL_HOME)/lib +MPICHLIB = +HDF5_INCL = -I$(HDF5_HOME)/include +HDF5_LIB = -L$(HDF5_HOME)/lib -Xlinker -R -Xlinker $(HDF5_HOME)/lib -lhdf5 -lz +OPT += -DNOCALLSOFSYSTEM +endif +# +# --------------------------------------------------------------------- +# +# LRZ: SuperMuc / LinuxCluster +# +# module load gsl/2.4 +# module load hdf5/serial +# +ifeq ($(TYPEOFSYSTEM),"LRZ") +CC = mpiicc +OPTIMIZE = -O3 -g -m64 +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -qopenmp +OMP_LIBS = -qopenmp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = $(GSL_INC) +GSL_LIBS = -L$(GSL_LIBDIR) +MPICHLIB = +HDF5_INCL = $(HDF5_INC) +HDF5_LIB = $(HDF5_SHLIB) -lhdf5 -lz +OPT += -DNOCALLSOFSYSTEM +endif +# +# --------------------------------------------------------------------- +# +# Dorc @ USM +# +# module load intel +# module load mpi.intel +# +ifeq ($(TYPEOFSYSTEM),"Dorc") +CC = mpiicc +OPTIMIZE = -O3 -g -Wall -Wno-uninitialized +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -fopenmp +OMP_LIBS = -lgomp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = -I/usr/local/include/ +GSL_LIBS = -L/usr/local/lib64/ +MPICHLIB = +HDF5_INCL = +HDF5_LIB = -lhdf5 -lz +endif +# +# --------------------------------------------------------------------- +# +# C2PAP +# +# module load gsl/2.4 +# module load hdf5/serial +# +ifeq ($(TYPEOFSYSTEM),"C2PAP") +CC = mpicc +OPTIMIZE = -O3 -g -m64 +ifeq (OPENMPTHREADS,$(findstring OPENMPTHREADS,$(CONFIGVARS))) +OPTIMIZE += -qopenmp +OMP_LIBS = -qopenmp +else +OPTIMIZE += -Wno-unknown-pragmas +endif +GSL_INCL = $(GSL_INC) +GSL_LIBS = -L$(GSL_LIBDIR) +MPICHLIB = +HDF5_INCL = $(HDF5_INC) +HDF5_LIB = $(HDF5_SHLIB) -lhdf5 -lz +OPT += -DNOCALLSOFSYSTEM +endif +# +####################################################################### +# Determine libraries that are not needed +# +ifneq (HDF5_SUPPORT,$(findstring HDF5_SUPPORT,$(CONFIGVARS))) +HDF5_LIB = +endif +# +####################################################################### +# +# +# +# +####################################################################### +# Define LINKER +####################################################################### +ifndef LINKER +LINKER = $(CC) +endif +####################################################################### +# +# +# +# +####################################################################### +# Object and header files +####################################################################### +OBJS = allocate.o allvars.o clustering.o fit.o functions.o galaxies.o \ + main.o output.o read_data.o read_trees.o setup.o statistics.o + +INCL += allvars.h proto.h + + + +####################################################################### +# Compile options +####################################################################### + +CCFLAGS = $(OPTIMIZE) $(OPT) $(GSL_INCL) $(HDF5_INCL) -I$(BUILD_DIR) +LDFLAGS = $(MATHLIB) $(OMP_LIBS) $(MPICHLIB) $(GSL_LIBS) $(GSLLIB) $(HDF5_LIB) + +OBJS := $(addprefix $(BUILD_DIR)/,$(OBJS)) $(BUILD_DIR)/compile_info.o +INCL := $(addprefix $(SRC_DIR)/,$(INCL)) $(BUILD_DIR)/codeoptions.h + + + +####################################################################### +# Build rules +####################################################################### + +all: $(EXEC) + +$(EXEC): $(OBJS) + $(LINKER) $(OBJS) $(LDFLAGS) -o $(EXEC) + $(CC) $(TOOLS_DIR)/convert_CT_to_emerge.c -o $(TOOLS_DIR)/convert_CT_to_emerge -lm + +clean: + rm -f $(OBJS) $(EXEC) + rm -f $(BUILD_DIR)/compile_info.c $(BUILD_DIR)/codeoptions.h + rm -f $(TOOLS_DIR)/convert_CT_to_emerge + +$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(INCL) $(MAKEFILES) + $(CC) $(CCFLAGS) -c $< -o $@ + +$(BUILD_DIR)/compile_info.o: $(BUILD_DIR)/compile_info.c $(MAKEFILES) + $(CC) $(CCFLAGS) -c $< -o $@ diff --git a/README.md b/README.md index 4b9874d..ab06cf5 100644 --- a/README.md +++ b/README.md @@ -1 +1,1490 @@ -# emerge \ No newline at end of file +# Emerge + + +Empirical model for the formation of galaxies + +Copyright © Benjamin P. Moster 2013-2019 + +License: GNU GPLv3 + +Science Paper: [http://arxiv.org/abs/1705.05373](http://arxiv.org/abs/1705.05373) + +Code Website: [http://www.usm.lmu.de/emerge](http://www.usm.lmu.de/emerge) + +Code Repository: [https://github.com/bmoster/emerge](https://github.com/bmoster/emerge) + +Gitter Community: [https://gitter.im/emerge-code](https://gitter.im/emerge-code) + +## Contents +1. [Introduction](#1-introduction) +2. [Basic Usage](#2--basic-usage) + 1. [Obtaining the code](#21--obtaining-the-code) + 2. [Compiling](#22--compiling) + 3. [Running the code](#23--running-the-code) + 4. [Different code modes](#24--different-code-modes) +3. [Configuration file](#3--configuration-file) + 1. [Code options](#31--code-options) + 2. [Model options](#32--model-options) + 3. [Data options](#33--data-options) + 4. [Correlation function options](#34--correlation-function-options) + 5. [Fitting options](#35--fitting-options) + 6. [Output options](#36--output-options) +4. [Parameter file](#4--parameter-file) + 1. [General Setup](#41--general-setup) + 2. [Input files](#42--input-files) + 3. [Cosmological parameters](#43--cosmological-parameters) + 4. [Simulation parameters](#44--simulation-parameters) + 5. [Units](#45--units) + 6. [Conversion efficiency parameters](#46--conversion-efficiency-parameters) + 7. [Satellite galaxy parameters](#47--satellite-galaxy-parameters) + 8. [Adjustable parameters](#48--adjustable-parameters) + 9. [Fitting parameters](#49--fitting-parameters) + 10. [MCMC parameters](#410--mcmc-parameters) + 11. [Output parameters](#411--output-parameters) +5. [Observed data files](#5--observed-data-files) + 1. [Stellar Mass Functions](#51--stellar-mass-functions) + 2. [Quenched Fractions](#52--quenched-fractions) + 3. [Cosmic Star Formation Rate Density](#53--cosmic-star-formation-rate-density) + 4. [Specific Star Formation Rates](#54--specific-star-formation-rates) + 5. [Projected correlation functions](#55--projected-correlation-functions) +6. [Halo merger tree files](#6--halo-merger-tree-files) +7. [Output files](#7--output-files) + 1. [Global statistics](#71--global-statistics) + 1. [Stellar Mass Functions](#711--stellar-mass-functions) + 2. [Quenched Fractions](#712--quenched-fractions) + 3. [Cosmic Star Formation Rate Density](#713--cosmic-star-formation-rate-density) + 4. [Specific Star Formation Rates](#714--specific-star-formation-rates) + 5. [Galaxy Clustering](#715--galaxy-clustering) + 6. [Model likelihoods](#716--model-likelihoods) + 7. [Adopted model parameters](#717--adopted-model-parameters) + 2. [Galaxy Catalogues](#72--galaxy-catalogues) + 3. [Halo Catalogues](#73--halo-catalogues) + 4. [Main Branches](#74--main-branches) +8. [Disclaimer](#8--disclaimer) + + +## 1) Introduction + +Emerge, an Empirical ModEl for the foRmation of GalaxiEs, populates dark matter halo merger trees +with galaxies using simple empirical relations between galaxy and halo properties. For each model +represented by a set of parameters, it computes a mock universe, which it then compares to +observed statistical data to obtain a likelihood. Parameter space can be explored with several +advanced stochastic algorithms such as MCMC to find the models that are in agreement with the +observations. + +The code is written in ANSI C and supports massively parallel computers with distributed memory. +Emerge uses an explicit communication model that is implemented with the standardized message +passing interface (MPI) and suports hyperthreading via the OpenMP interface. It can be run on +essentially all supercomputer systems, including clusters of workstations or individual personal +computers. + +The main reference for the scientific methods that are used in this code is the paper +`Emerge - An empirical model for the formation of galaxies since z ~ 10' (Moster, Naab & +White 2018), and references therein. This document provides additional technical information +about the code to allow anyone interested an independent use of the code on parallel machines, +and to create mock galaxy catalogues for any dark matter simulation. For further help, visit +the code website at [usm.lmu.de/emerge](http://www.usm.lmu.de/emerge) or join the Gitter +community at [gitter.im/emerge-code](https://gitter.im/emerge-code). + +Emerge was written by Benjamin Moster, and is made publicly available under the GNU general +public license version 3. This implies that you may freely copy, distribute, or modify the sources, +but the copyright for the original code remains with the author. If you find the code useful for +your scientific work, we kindly ask you to include a reference to the Emerge paper in all studies +that use data obtained with Emerge. + + +## 2) Basic Usage + +#### 2.1) Obtaining the code + +Emerge is hosted on [GitHub](https://github.com/bmoster/emerge) and can be obtained either +by cloning the git repository + + git clone https://github.com/bmoster/emerge.git + +or by downloading a compressed tarball of the latest version: + +https://github.com/bmoster/emerge/archive/master.zip (~ __700kB__). + + +#### 2.2) Compiling + +Emerge needs the following non-standard libraries for compilation: +- __MPI__ - the Message Passing Interface. There are numerous commercial versions, in addition to +excellent open source implementations, e.g. + MPICH (http://www.mpich.org), or + Open MPI (http://www.open-mpi.org). +- __GSL__ - the GNU scientific library. This open-source package can be obtained at + http://www.gnu.org/software/gsl, +for example. Emerge needs this library for spline interpolations, and for random number generation. +- __HDF5__ - the Hierarchical Data Format (version 5.0 or higher, available at + https://www.hdfgroup.org). +This optional library is only needed when one wants to write snapshot files in HDF5 format. +It is possible to compile and use the code without this library. + +On most UNIX systems installing these libraries should only take a few steps. Untar the downloaded tarball +and change to the newly created directory. Run the configuration script by typing `./configure`, compile the code +by typing `make all`, and install the libraries by tyiping `sudo make install`. + +After downloading the Emerge code, you will find several files and folders: +The `build` folder is initially empty, but will host the object files after compilation. +The `data` folder contains all files that store observed data (see [Observed data files](#5--observed-data-files) for more details). +The `output` folder is initially empty, but all output data will be written to this folder, see [Output files](#7--output-files) for more details. +The `parameterfiles` folder contains an example parameter file (see [Parameter file](#4--parameter-file) for more details). +The `src` folder contains the source code including all c-files and h-files. +The `tools` folder contains files that convert Rockstar halo merger trees to the Emerge binary input format, and a sample +script file to submit to a computing queue. +The `trees` folder is initially empty and can be used to store the input halo merger trees. +The files `compile-config.perl` and `config-makefile` are needed for compiation, but do not need to be edited. +The file `Template-Config.sh` has to be copied to `Config.sh` and contains all compile-time options. Edit `Config.sh` +as needed (see [Configuration file](#3--configuration-file) for more details). +The file `Template-TypeOfSystem.sh` has to be copied to `TypeOfSystem.sh` and contains a number of predefined systems. +The appropriate system should be uncommented in `TypeOfSystem.sh`. By following these example systems, further customised +system-types can be easily added to the `Makefile`. Slight adjustments will be needed if any of the above libraries is not +installed in a standard location on the system. Also, compiler optimisation options may need adjustment, depending on the +C-compiler that is used. The executable `emerge` can then be built by typing `make`. + + +#### 2.3) Running the code + +To run Emerge, invoke the executable with a command like + + mpiexec -np 16 ./emerge emerge.param + +This will start emerge using 16 computing cores and with parameters as specified in the parameter file `emerge.param` +(see [Parameter file](#4--parameter-file) for more details). The code does not need to be recompiled for a different number +of processors, for different parameters, or for different halo marger trees. Emerge can also be run with a single computing +core given enough memory for the problem size. To this end, the leading `mpiexec -np 1` can be omitted such that Emerge +behaves like a serial code. The MPI library must be installed in any case as it is frequently referenced in the source code. +The number of processors can be chosen freely, however it must be a multiple of the number of universes that are computed at +the same time. This means that if for example 20 universes are computed in the same step (e.g. for the parallelised MCMC), +at least 20 cores are needed, or a multiple thereof. + +While Emerge is running, it prints out several log messages that inform about the present steps taken by the code. +If Emerge is used interactively in a terminal these messages appear on the screen. They can be redirected to a file by adding +`> log.out` at the end of the execution command. If Emerge is used to explore parameter space, or if the problem size is too +large to fit on a single computing node, it will typically need to be run on a supercomputer. In this case, the above command +has to be put into a script file that can be submitted to the computing queue, and the output should be diverted to a file. +The sample script file `submit` in the `tools` folder shows how to do this for the SuperMuc computer at the LRZ, but will need +to be adapted for a different supercomputer. + + +#### 2.4) Different code modes + +Invoking the execution command above will run Emerge for the halo merger trees and parameters specified in the parameter file +once, and write the resulting galaxy properties into files in the `output` directory. However, Emerge can also be used to explore +parameter space with different algorithms. To this end, an optional flag can be added to the end of the execution command. The +three currently implemented parameter space exploration methods are: + +Flag | Mode +:--: | ---- +1 | Affine-invariant MCMC ensemble sampler by Goodman & Weare (2010) +2 | HYBRID optimisation algorithm by Elson et al. (2007) +3 | Parallel-tempering as laid out by Sambridge (2014) + +The HYBRID optimisation algorithm for example can thus be started by invoking + + mpiexec -np 16 ./emerge emerge.param 2 + +which will first initialise a number of walkers in a parameter range as specified in the parameter file, and then evolve the walker +positions according to the algorithm and several hyperparameters. The number of walkers, i.e. universes, that are computed during +one chain step and the time limit of the run can also be specified in the parameter file. Each set of chains requires a seed number +to be specified in the parameter file. This number will be included in the name of the output files, such that they can be distinguished, +e.g. for the seed number `12345` the MCMC output file name will be `mcmc12345.000.out`, the HYBRID output file name will be +`hybrid12345.000.out`, and the parallel-tempering output file name will be `pt12345.000.out`. + +Often a single submission to a queueing system will not provide enough time to explore parameter space as thoroughly as needed. +However, all algorithms store the current walker positions in dedicated restart files in the `output` folder, e.g. `walkers.mcmc.12345.out`. +Emerge can then be restarted from these walker positions and the chains will then be continued. To resume parameter space exploration +with a specific algorithm a `1` has to be added to the flag above, e.g. the parallel-tempering algorithm can be restarted from the +latest saved walker positions by invoking + + mpiexec -np 16 ./emerge emerge.param 31 + +The appropriate restart file `walkers.pt.12345.out` has to exist and have the required number of walkers. Once the code restarts, it +will write the walker postions to a new output file, e.g. `pt12345.001.out`. Restarting Emerge in this way is transparent to the code, +i.e. after the restart Emerge behaves exactly as if had not been interrupted in the first place. When the code is started with a restart +flag, the parameter file is parsed, but only some of the parameters are allowed to be changed, while any changes in the others will be +ignored. Which parameters these are is explained in the section [Parameter file](#4--parameter-file) . + + +## 3) Configuration file + +Many aspects of the Emerge code are controlled with compile-time options in the Config.sh file rather than run-time options in the +parameter file. In this way, highly optimised binaries for a specific model can be generated by the compiler. However, the disadvantage +of this approach is that different setups may require different binary executables. If several models are run concurrently, there is +hence the danger that Emerge is started or resumed with the `wrong' binary. While Emerge checks the plausibility of some of the most +important code options, this is not done for all of them. To minimise the risk of using the wrong code, it is therefore recommended +to produce a separate executable for each simulation that is run. This can be easily achieved by creating an alternative Config.sh +file for each setup, and then compiling the source code invoking + + make CONFIG=NewConfig.sh EXEC=emerge_new + +The Config.sh file contains a list of all available compile-time code options, with most of them commented out by default. To activate +a certain option, it should be commented in, and given the desired value, where appropriate. Whenever one of the Config.sh options +described below is changed, a full recompilation of the code is necessary, which can be achieved with a simple `make` command. +A manual recompilation of the whole code can be enforced with the command `make clean`, which will erase all object files, followed by `make`. + + +#### 3.1) Code options + +`OPENMPTHREADS=2` +> Enables OpenMP support and sets the number of threads per task. If the computing system supports hyperthreading the default +> value of 2 is recommended. + +`RANDOM_NUMBER_TABLE=1000000` +> If this option is set, random number tables will be created at the start of the run, for a uniform distribution and a gaussian +> distribution, respectively. The parameter has to be set to the number of random numbers each of these tables will contain. The +> random numbers created in this way will be only used for the scatter between intrinsic and observed galaxy properties and for +> the positions of orphan galaxies within their host haloes, not for the parameter space exploration algorithms. The +> advantage of this option is the increased computation speed, as random numbers do not have to be recomputed for each galaxy in +> each chain step, and the consistent scatter between steps. + +`LONGIDS` +> If this option is set, the code assumes that IDs for haloes, descendants, hosts, and forests, are stored as 64-bit long integers. +> This is only really needed if there are more than ~ 2 billion haloes in the merger trees. + +`DISABLE_MEMORY_MANAGER` +> By default, Emerge is using a memory manager that handles the dynamic memory allocation. The overhead for the allocation and +> dellocation of memory during the run is avoided by allocating a large part of the total available memory during initialisation. +> This section is then filled dynamically by individual memory blocks using a stack structure and a 64 bit boundary. +> If this option is set, the memory manager is disabled, and all memory blocks are allocated on the fly, which may reduce +> the performance of the code. + + +#### 3.2) Model options + +`SFE_MPEAK_ZEVOLV` +> By default, the instantaneous conversion efficiency ε(M) = 2 εN [ (M/M1) + (M/M1)γ]-1 +> does not depend on redshift. Setting this option makes the characteristic halo mass M1 depend linearly on the scale factor a: +> M1(z) = M0 + Mz(1-a) = M0 + Mz ∙ z/(z+1) . + + +`SFE_NORM_ZEVOLV` +> Setting this option makes the normalisation of the instantaneous conversion efficiency εN depend linearly on the scale +> factor a: +> εN(z) = ε0 + εz(1-a) = ε0 + εz ∙ z/(z+1) . + + +`SFE_BETA_ZEVOLV` +> Setting this option makes the low-mass slope of the instantaneous conversion efficiency β depend linearly on the scale +> factor a: +> β(z) = β0 + βz(1-a) = β0 + βz ∙ z/(z+1) . + + +`SFE_GAMMA_ZEVOLV` +> Setting this option makes the high-mass slope of the instantaneous conversion efficiency γ depend linearly on the scale +> factor a: +> γ(z) = γ0 + γz(1-a) = γ0 + γz ∙ z/(z+1) . + + +`SFE_SAME_SLOPE` +> If this option is set, the low and high-mass slopes of the instantaneous conversion efficiency are required to have the same absolute +> value at any redshift, i.e. β(z)=γ(z). + +`SAT_QUENCH_MASS_DEPENDENT` +> By default, satellite galaxies are quenched after a fixed time τ once their halo stops growing, given by +> τ = τ0 ∙ tdyn, where tdyn=Rvir/Vvir is the dynamical +> time of the halo. If this option is enabled, the quenching time τ depends on the stellar mass of the galaxy, with +> low-mass galaxies having a larger quenching time, given by +> τ = τ0 ∙ tdyn ∙ max [(m* / 1010M)s,1] , + +> where the quenching slope τs is a free parameter. + +`SAT_STRIP_USE_MSTAR` +> By default, a satellite galaxy gets stripped and its stellar mass added to the ICM of the main halo, if the mass of its subhalo +> drops below a fraction of the peak mass during its history: M < fs ∙ Mpeak. If this option is set, +> satellite galaxies are instead stripped if the mass of their subhalo drops below a fraction of their own stellar mass: +> M < fs ∙ m*. + +`SAT_SFR_EXP_DECAY` +> If this option is selected, the star formation rate of a galaxy declines exponentially on a timescale τd after +> its halo has reached its peak mass: +> d/dt m* = d/dt m*,peak ∙ exp [-τd (t-tpeak) / tdyn] . + + +`COMPUTE_ICM` +> This option enables the computation of the ICM, and requires the halo merger trees to be organised in forests. A forest contains all +> merger trees that are in contact through their history, i.e. if a halo is located within the virial radius of another halo at any point +> in time, their merger trees are part of the same forest. To be able to handle forests, Emerge requires a forest file with the same base +> file name as the merger tree files and a `.forests` file extension. This file has to store the tree ID for each merger tree (first column), +> and the corresponding forest ID (second column). Emerge then distributes the merger trees to computing cores such that each forest is +> loaded on a single core. These additional sorting processes require somewhat more time at the start of Emerge. + +`DF_USE_BK08` +> If this option is set, Emerge uses the dynamical friction formula by Boylan-Kolchin et al. (2008). Otherwise, the standard formula +> by Binney & Tremaine (1987) is used. + +`ORPHAN_MASSLOSS` +> This option only affects orphan galaxies, i.e. galaxies for which the corresponding subhalo has dropped below the resolution limit of +> the dark matter simulation. If enabled, the halo associated with an orphan galaxy loses mass at the same rate as it did between +> reaching its peak mass and dropping below the resolution limit. Otherwise, the halo mass of an orphan galaxy stays constant at the +> mass when the halo got lost. + +`ORPHAN_NONRADIAL_INFALL` +> By default, the position of orphan galaxies is chosen to be on fixed angular coordinates with respect to the central galaxy, while +> the radius decreases with the dynamical friction time. If this option is set, the angular coordinates are no longer fixed, but chosen +> randomly on the unit sphere at each time step. + + +#### 3.3) Data options + +`READ_SMF` +> If this option is set, the stellar mass functions are read from the corresponding file in the `data` folder, and included in the +> computation of the model likelihood. + +`READ_FQ` +> If this option is set, the fractions of quenched galaxies as a function of stellar mass are read from the corresponding file in +> the `data` folder, and included in the computation of the model likelihood. + +`READ_CSFRD` +> If this option is set, the cosmic star formation rate densities are read from the corresponding file in the `data` folder, and +> included in the computation of the model likelihood. + +`READ_SSFR` +> If this option is set, the specific star formation rates as a function of stellar mass are read from the corresponding file in +> the `data` folder, and included in the computation of the model likelihood. + +`READ_WP` +> If this option is set, the projected galaxy auto-correlation functions are read from the corresponding file in the `data` folder, +> and included in the computation of the model likelihood. + +`GLOBAL_SIGMA_SMF` +> If this option is set, a global error between different measurements of the stellar mass function can be specified in the parameter +> file. This value will then be added in quadrature to the uncertainty of each individual data point. + +`GLOBAL_SIGMA_FQ` +> If this option is set, a global error between different measurements of the quenched fraction can be specified in the parameter +> file. This value will then be added in quadrature to the uncertainty of each individual data point. + +`GLOBAL_SIGMA_CSFRD` +> If this option is set, a global error between different measurements of the cosmic star formation rate density can be specified in +> the parameter file. This value will then be added in quadrature to the uncertainty of each individual data point. + +`GLOBAL_SIGMA_SSFR` +> If this option is set, a global error between different measurements of the specific star formation rate can be specified in the parameter +> file. This value will then be added in quadrature to the uncertainty of each individual data point. + +`GLOBAL_SIGMA_WP` +> If this option is set, a global error between different measurements of the projected galaxy auto-correlation function can be +> specified in the parameter file. This value will then be added in quadrature to the uncertainty of each individual data point. + + +#### 3.4) Correlation function options + +`WP_RBINS=20` +> To compute the projected galaxy auto-correlation function, first the 3D auto-correlation function is computed. This option can be +> used to override the default number of 20 radial bins. + +`WP_RBINS_INT=1000` +> The projected auto-correlation function is integrated from the 3D function using interpolation. This option can be used to override +> the default number of 1000 interpolation points. + +`WP_RMAX=0.1` +> The correlation function is computed up to a maximum distance of `WP_RMAX` times the box side length. This option can be used to override +> the default value of 0.1. + +`WP_NLEAF_MIN=4` +> The pair counting needed to compute the correlation function uses a kd-tree. While building this tree, a node is split if it contains +> more than 4 objects. This option can be used to override the default value. + +`WP_NODE_WIDTH_MIN=0.01` +> The splitting of nodes while building the kd-tree needed for the correlation function is also stopped, if the widest dimension is +> smaller than 0.01 times the box side length. This option can be used to override the default value. + + +#### 3.5) Fitting options + +The HYBRID optimisation algorithm uses a combination of particle swarm optimisation and simulated annealing, and the step size depends +on the likelihood of the walker with respect to the other walkers and the starting likelihood. At each MCMC step i, +the mean χ2 value is computed and compared to the mean starting χ2 value: +qi=<χi2>/<χ12>. Moreover, the χ2 value of each individual walker j is compared +to the mean χ2 value: pj=<χj2>-χj2. The step size of each walker is then chosen as +σ = σ0(T) ∙ g(q) ∙ f(p), where σ(T) is a normalisation that can depend on the chain temperature, +and g and f are monotonic functions. The dependence on q is implemented as g(q)=qα, while the dependence +on p is implemented as f(p)=1-β p for p<0 and f(p)=(p+1) for p>= 0. + + + +`HYBRID_ALPHA=0.4` +> This option can be set to override the default value of 0.4 for the slope α of the function g(q). For a larger value, +> the step size will depend more strongly on the ratio between current and initial mean likelihood. + +`HYBRID_BETA=1.0` +> This option can be set to override the default value of 1.0 for the slope β of the function f(p) for p<0. For a +> larger value the step size will depend more strongly on the difference between the likelihood of the walker and the current mean likelihood. + +`HYBRID_GAMMA=1.0` +> This option can be set to override the default value of 1.0 for the slope γ of the function f(p) for p >= 0. For a +> larger value the step size will depend more strongly on the difference between the likelihood of the walker and the current mean likelihood. + +The parallel-tempering algorithm runs MCMC chains at different temperatures and can swap the temperature of two walkers if one of them +has found a location with a good likelihood. In this way the hot walkers can explore a large part of the parameter space quickly, while +cold walkers explore the regions with a high likelihood more thoroughly. At the end of the run, the cold walkers with T=1 sample +the posterior distribution. A fraction of the walkers is set to T=1, while the other walkers follow a logarithmic temperature +distribution with a maximum temperature Tmax that can be specified in the parameter file. + + + +`PT_COLD_FRACTION=0.25` +> This option can be set to override the default 25% fraction of cold walkers with T=1. + +`PT_TARGET_ACCEPTANCE=0.3` +> The step size for new walker postions is adjusted on the fly, such that the resulting acceptance fraction has a specified value. +> With this option the default value for the desired acceptance fraction of 30% can be overridden. + +`PT_STEPS_SCALE_ADJUST=10` +> The step size for new walker postions is adjusted is adjusted every 10 steps by default. This option can be set to override this choice. + + +#### 3.6) Output options + +`HDF5_SUPPORT` +> This option enables support for the HDF5 file format. Note that it can only be used, if the HDF5 library is installed and properly +> linked in the `Makefile`. If this option is set, the desired output file format can be specified in the parameter file. Otherwise +> all output files will bin in ASCII fromat. + +`WRITE_GALAXY_CATALOG` +> If this option is set, Emerge writes galaxy catalogues to the `output` folder. The output redshifts, stellar mass threshold, number +> of files per redshift, and output format can be specified in the parameter file for each run. For details on the structure of the +> galaxy catalogues see [Galaxy Catalogues](#72--galaxy-catalogues) . + +`WRITE_HALO_CATALOG` +> If this option is set, Emerge writes dark matter halo catalogues to the `output` folder for the same redshifts, stellar mass threshold, +> number of files per redshift, and output format as the galaxy catalogues. For details on the structure of the halo catalogues see +> [Halo Catalogues](#73--halo-catalogues) . + +`WRITE_MAINBRANCH` +> If this option is set, Emerge writes the main branch, i.e. only main progenitors and descendants for seleced systems to the +> `output` folder. These galaxies are seleced at a specific redshift and a stellar or halo mass bin in the parameter file. + + + +## 4) Parameter file + +Many aspects of Emerge do not need to be set before compilation, but can be specified at run-time in a parameter file whenever +the code is started. Each parameter is set by first specifying a keyword and then a numerical value or a character string, separated +by spaces or tabs. A separate line has to be used for each keyword, and the corresponding value has to appear in the same line. +It is possible to use an arbitrary amount of space between keywords, including empty lines. The keywords can appear in any order, +but can only appear once. If a necessary keyword has been omitted, or a keyword that is not needed has been set, the code will +print an error message, and the parameter file has to be adjusted. All keywords are type-sensitive. Empty lines, or lines with +a leading `%` are ignored. Comments can be added after the value for the corresponding keyword. + +This section discusses the parameters in detail. All keywords marked with an asterisk will be ignored if Emerge is restarting +a fitting algorithm, as these values are loaded from the restart file. In this case it is typically not needed to change the +other parameters. + + +#### 4.1) General Setup + +`ModelName`       `P100` +> This is the name of the model. A folder with the same name will be created in the `output` folder, and all output +> files will be written to this folder. + +`UniversesInParallel`       `1` +> This specifies the number of universes, i.e. walkers, that will be computed in parallel. If more than one universe are +> computed in parallel, the initial setup of the halo merger trees will be done for the first universe and then duplicated +> for the other universes. For a simple generation of mock catalogues, this parameter can be set to `1`. For parameter +> space exploration, ideally the number of computing cores per universe, i.e. the total number of available cores divided +> by the number of parallel universes, is chosen such that it fits in the available memory per core. This setup results in +> as many parallel universes as possible and will typically lead to the best performance. For example, if 400 cores are +> available, and at least 10 cores are needed to hold one universe, `UniversesInParallel` should be set to 40. Note that +> the number of cores must be a multiple of `UniversesInParallel`. + +`NumFilesInParallel`       `4` +> The code can read halo merger tree files and write output files in parallel. This parameter controls how many files are +> processed in parallel, and is ideally set to a subdivision of the number of halo merger tree files. + +`Verbose`       `1` +> This parameter regulates how much screen output is produced. Currently it can be set to 1 (minimal screen output), +> and 2 (maximal screen output). + +`MaxMemSize`       `1500` +> This parameter specifies the amount of memory in MByte that the dynamic memory manager allocates for each computing +> core. This should typically be set to the amount of available memory divided by the number of cores. If `MaxMemSize` has been +> chosen too low, the code will print an error message and inform how much memory is needed for the specified setup. +> In case the available memory per core is lower than this value, either the number of parallel universes must be decreased, +> or more memory is needed, i.e. additional computing nodes must be added. + +`BufferSize`       `100` +> This parameter specifies the size in Mbyte for the multi-purpose communication buffer used by the code in various parts of the +> parallel algorithms. It should be large enough to have minimal work-load imbalance losses, though in practice a few to 100 MBytes +> are sufficient. + + +#### 4.2) Input files + +`TreefileName`       `trees/P100` +> This sets the filename of the halo merger tree files to be read in at start-up. The trees can be distributed into several files. +> In this case, only the base name without the tailing .n number should be specified. The code will recognise the number of files +> that contain the halo merger trees automatically, and load all of these files accordingly. In the example above, the code will +> read the files `P100.0`, `P100.1`, and so on, in the `trees` folder. + +`SMFfileName`       `data/smf.dat` +> This sets the location and the name of the file that contains the observed stellar mass functions. For details on the structure +> of this file, see [Stellar Mass Functions](#51--stellar-mass-functions) . + +`FQfileName`       `data/fq.dat` +> This sets the location and the name of the file that contains the observed fractions of quenched galaxies as function of stellar mass +> and redshift. For details on the structure of this file, see [Quenched Fractions](#52--quenched-fractions) . + +`CSFRDfileName`       `data/csfrd.dat` +> This sets the location and the name of the file that contains the observed cosmic star formation rate densities. +> For details on the structure of this file, see [Cosmic Star Formation Rate Density](#53--cosmic-star-formation-rate-density) . + +`SSFRfileName`       `data/ssfr.dat` +> This sets the location and the name of the file that contains the observed specific star formation rates as function of stellar mass +> and redshift. For details on the structure of this file, see [Specific Star Formation Rates](#54--specific-star-formation-rates) . + +`WPfileName`       `data/wp.dat` +> This sets the location and the name of the file that contains the observed projected auto-correlation functions. For details on the +> structure of this file, see [Projected correlation functions](#55--projected-correlation-functions) . + + +#### 4.3) Cosmological parameters + +These parameters specify the cosmology and have to take the same values that have been used to simulated the halo merger trees. + + + + +`HubbleParam`       `0.6781` +> This sets the value of the Hubble constant in units of 100 kms -1 Mpc -1. + +`Omega0`       `0.3080` +> This sets the value of the cosmological matter density parameter in units of the critical density at z=0. + +`OmegaLambda`       `0.6781` +> This sets the value of the cosmological dark energy density parameter for a cosmological constant in units of the critical +> density at z=0. A geometrically flat universe has `Omega0 + OmegaLambda = 1`. + +`OmegaBaryon`       `0.0484` +> This sets the value of the cosmological baryon density parameter in units of the critical density at z=0. The resulting +> value for universal baryon fraction is given by `Omega0 / OmegaLambda`. + + +#### 4.4) Simulation parameters + +`BoxSize`       `67.81` +> This parameter specifies the side length of the simulation box from which the dark matter halo merger trees have been extracted. +> Note that it should have the same unit as the tree files, e.g. if the side length is 100 Mpc, but the unit used for the merger +> trees is Mpc/h, the value given here should be `67.81` (for h = 0.6781). + + +#### 4.5) Units + +These parameters set the internal units. All simulated and observed data that is read in will be converted to these internal units. +The output data will be converted to convenient units (see [Output files](#7--output-files) for more details). + +`UnitLength_in_Mpc`       `1.0` +> This sets the internal length unit in Mpc. + +`UnitTime_in_yr`       `1.0e9` +> This sets the internal time unit in years. The above choice is convenient for cosmological boxes as it sets the time unit to one Gyr. + +`UnitMass_in_Msun`       `1.0e9` +> This sets the internal mass unit in solar masses. The above choice is convenient, as the interal unit for the star formation rate +> is then one solar mass per year. + + +#### 4.6) Conversion efficiency parameters + +These parameters regulate the instantaneous baryon conversion efficiency. If Emerge is run with a mode for which only one model is +computed, the exact values specified are used. If Emerge is run with a mode for which walkers with random initial positions are +evolved, the values specify the mean of the bin from which the inital values for the walkers are sampled. + +`Eff_MassPeak`       `11.35` +> This sets the z=0 value M0 for the parameter M1 of the conversion efficiency, which specifies the halo mass +> close to which the conversion efficiency peaks. + +`Eff_Normalisation`       `0.009` +> This sets the z=0 value ε0 for the parameter εN of the conversion efficiency, +> which specifies its normalisation. + +`Eff_LowMassSlope`       `3.09` +> This sets the z=0 value β0 for the parameter β of the conversion efficiency, which specifies its +> low-mass slope. + +`Eff_HighMassSlope`       `1.11` +> This sets the z=0 value γ0 for the parameter γ of the conversion efficiency, which specifies its +> high-mass slope. This parameter is not present, if the code has been compiled with the option `SFE_SAME_SLOPE`. + +`Eff_MassPeak_Z`       `0.65` +> This sets the redshift scaling Mz for the parameter M1 of the conversion efficiency. This parameter is +> only present if the code has been compiled with the option `SFE_MPEAK_ZEVOLV`. + +`Eff_Normalisation_Z`       `0.60` +> This sets the redshift scaling εz for the parameter εN of the conversion efficiency. +> This parameter is only present if the code has been compiled with the option `SFE_NORM_ZEVOLV`. + +`Eff_LowMassSlope_Z`       `-2.02` +> This sets the redshift scaling βz for the parameter β of the conversion efficiency. This parameter +> is only present if the code has been compiled with the option `SFE_BETA_ZEVOLV`. + +`Eff_HighMassSlope_Z`       `0.0` +> This sets the redshift scaling γz for the parameter γ of the conversion efficiency. This parameter +> is only present if the code has been compiled with the option `SFE_GAMMA_ZEVOLV`, and the option `SFE_SAME_SLOPE` has not been set. + + +#### 4.7) Satellite galaxy parameters + +These parameters regulate the evolution of satellite galaxies. If Emerge is run with a mode for which only one model is +computed, the exact values specified are used. If Emerge is run with a mode for which walkers with random initial positions are +evolved, the values specify the mean of the bin from which the inital values for the walkers are sampled. + +`Fraction_Escape_ICM`       `0.56` +> This sets the escape fraction for galaxy mergers, i.e. the fraction of the stellar mass of the satellite galaxy that gets ejected +> into the ICM during a galaxy merger. + +`Fraction_Stripping`       `0.004` +> This parameter defines the threshold for the subhalo mass below which satellites are instantly stripped to the ICM. If the option +> `SAT_STRIP_USE_MSTAR` is set, satellite galaxies are stripped if the mass of their subhalo drops below `Fraction_Stripping` times +> their own stellar mass, otherwise they are stripped if the mass of their subhalo drops below `Fraction_Stripping` times it maximum +> mass through history. + +`Timescale_Quenching`       `4.46` +> This sets the timescale for satellite quenching. If a subhalo has dropped below its maximum mass through history for more than +> `Timescale_Quenching` times the halo dynamical time, the SFR in the satellite galaxy is set to zero. + +`Timescale_Quenching`       `0.35` +> If the option `SAT_QUENCH_MASS_DEPENDENT` has been set, this parameter defines the slope τs of the mass +> dependence of the quenching timescale +> τ = τ0 ∙ tdyn ∙ max [(m* / 1010M)s,1] . + + +`Decay_Quenching`       `0.0` +> If the option `SAT_SFR_EXP_DECAY` has been set, this parameter sets the timescale for the exponential decay of the SFR after the +> subhalo falls below its maximum mass. After the quenching timescale τ has elapsed, the SFR is still set to zero. + + +#### 4.8) Adjustable parameters + +`Mstar_min`       `7.0` +> Minimum logarithmic stellar mass in solar masses that is used to compute global statistics such as stellar mass functions, quenched +> fractions and specific star formation rates. All observational data below this mass are ignored in the fit. + +`Mstar_max`       `12.5` +> Maximum logarithmic stellar mass in solar masses that is used to compute global statistics such as stellar mass functions, quenched +> fractions and specific star formation rates. All observational data above this mass are ignored in the fit. + +`Delta_Mstar`       `0.2` +> Stellar mass bin size in dex used to compute global statistics. During the fitting process the model values corresponding to individual +> oberved data will be interpolated from these bins. + +`Observation_Error_0`       `0.08` +> In Emerge, each galaxy has an *intrinsic* stellar mass, i.e. the physically self-consistent stellar mass given by the integral of past +> star formation and mass loss, and an *observed* stellar mass that is computed from the intrinsic mass and a gaussian distribution for +> the observational error. The standard deviation of this distribution linearly depends on redshift as +> σ = σ0z∙ z. The z=0 scatter σ0 is set by `Observation_Error_0`. + +`Observation_Error_z`       `0.06` +> The scatter between *intrinsic* and *observed* stellar mass increases linearly with redshift. The slope σz +> is set by `Observation_Error_z`. The scatter increases only up to redshift of z=4, after which it is held constant. This +> choice can be changed by modifying the value of `ZMAX_SMFERR` in the `allvals.h` file. + +`Global_Sigma_SMF_LZ`       `0.15` +> If the option `GLOBAL_SIGMA_SMF` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the stellar mass function below z=0.1. This redshift threshold can be changed by modifying the value of `SIGMASMFZTHRESH` in +> the `allvals.h` file. This increased uncertainty can be sensible if different selected observational data sets are not in agreement with +> each other given their original uncertainties. + +`Global_Sigma_SMF_HZ`       `0.30` +> If the option `GLOBAL_SIGMA_SMF` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the stellar mass function above z=0.1. + +`Global_Sigma_FQ`       `0.10` +> If the option `GLOBAL_SIGMA_FQ` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the quenched fractions. + +`GLOBAL_SIGMA_CSFRD`       `0.10` +> If the option `GLOBAL_SIGMA_CSFRD` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the cosmic SFR density. + +`GLOBAL_SIGMA_SSFR`       `0.15` +> If the option `GLOBAL_SIGMA_SSFR` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the specific SFRs. + +`GLOBAL_SIGMA_WP`       `0.40` +> If the option `GLOBAL_SIGMA_WP` has been set, this parameter is added in quadrature to the observational uncertainty of each data point +> of the projected galaxy auto-correlation functions. + + +#### 4.9) Fitting parameters + +When the model parameters are fit using any of the fitting algorithms (code modes 1, 2, or 3), the walkers are initialised uniformly in a +hyper-cube centred around the values given above. The width of the cube in each dimension is given by the following parameters, which have +the same names as the fitted parameters plus an extension of `_Range`. For example, if the fitted parameter has a value of 1.0 and the +`_Range` parameter has a value of 0.5, the walkers will be initialised uniformly between 0.75 and 1.25 in this dimension. Note that some +parameters are fit in logarithmic space, as indicated below. + +`Eff_MassPeak_Range`       `0.2` +> Range of the initial distribution of the parameter `Eff_MassPeak`. + +`Eff_Normalisation_Range`       `0.5` +> Range of the initial distribution of the parameter `Eff_Normalisation`. This parameter is fit in logarithmic space, and the range is +> given in dex. For example, if `Eff_Normalisation` is 0.01 and `Eff_Normalisation_Range` is 0.5, the walkers will be distributed +> logarithmically between 10^[log10(0.01) - 0.5/2] = 0.0056 and 10^[log10(0.01) + 0.5/2] = 0.0178. + +`Eff_LowMassSlope_Range`       `0.5` +> Range of the initial distribution of the parameter `Eff_LowMassSlope`. + +`Eff_HighMassSlope_Range`       `0.2` +> Range of the initial distribution of the parameter `Eff_HighMassSlope`. + +`Eff_MassPeak_Z_Range`       `0.2` +> Range of the initial distribution of the parameter `Eff_MassPeak_Z`. + +`Eff_Normalisation_Z_Range`       `0.2` +> Range of the initial distribution of the parameter `Eff_Normalisation_Z`. + +`Eff_LowMassSlope_Z_Range`       `0.5` +> Range of the initial distribution of the parameter `Eff_LowMassSlope_Z`. + +`Eff_HighMassSlope_Z_Range`       `0.2` +> Range of the initial distribution of the parameter `Eff_HighMassSlope_Z`. + +`Fraction_Escape_ICM_Range`       `0.3` +> Range of the initial distribution of the parameter `Fraction_Escape_ICM` in dex, distributed logarithmically. + +`Fraction_Stripping_Range`       `0.3` +> Range of the initial distribution of the parameter `Fraction_Stripping` in dex, distributed logarithmically. + +`Timescale_Quenching_Range`       `0.5` +> Range of the initial distribution of the parameter `Timescale_Quenching` in dex, distributed logarithmically. + +`Slope_Quenching_Range`       `0.4` +> Range of the initial distribution of the parameter `Slope_Quenching`. + +`Decay_Quenching_Range`       `0.2` +> Range of the initial distribution of the parameter `Decay_Quenching`. + + +#### 4.10) MCMC parameters + +For parameter space exploration, three different code modes can be used. The following hyperparameters regulate these algorithms. + +`NumberOfMCMCWalkers`       `8` +> This sets the number of walkers used for all parameter space exploration methods. Each walker then moves through parameter space +> for each step of the algorithm, forming a chain. As all parameter space exploration algorithms are ensemble methods, a large +> number here is recommended, ideally several hundreds of walkers. + +`MCMCScaleParameter`       `2.0` +> This sets the step size with which new walker positions will be suggested. For the affine-invariant MCMC sampler (code mode 1), +> `MCMCScaleParameter` defines the a parameter and is suggested to be chosen around 2. For the HYBRID method +> (code mode 2) and the parallel tempering algorithm (code mode 3), this parameter sets the initial gaussian trial step, based on the +> parameter range divided by two to the power of `MCMCScaleParameter`. For example, if the parameter range `Eff_MassPeak_Range` is +> set to 0.2, and `MCMCScaleParameter` is set to 4, a new trial step size will be drawn from a gaussian distribution with a sigma +> of 0.2 / 24 = 0.0125 for the dimension corresponding to the parameter `Eff_MassPeak`. A higher value leads to +> a larger step size, and consequently to a lower acceptance rate. For code modes 1 and 2 this parameter can be adjusted to obtain +> the desired acceptance rate, while for code mode 3, the step size is adjusted on the fly to get an acceptance rate as defined by +> the option `PT_TARGET_ACCEPTANCE`. + +`ChainTemperature`       `100.0` +> This sets the initial temperature for the chains. If the code is run with mode 1 (MCMC), this parameter is ignored. If the code is +> run with mode 2 (HYBRID), `ChainTemperature` sets the initial temperature of all chains. For a positive value, this temperature will +> decrease on a logarithmic timescale. For a negative value, the temperature will be held constant at the absolute value of +> `ChainTemperature`. If the code is run with mode 3 (parallel tempering), `ChainTemperature` sets the temperature of the hottest chain. +> A fraction of the walkers (set with the option `PT_COLD_FRACTION`) is initialised at a temperature of T=1, while the remaining +> walkers will have temperatures spaced logarithmically up to the value of `ChainTemperature`. + +`TimeLimit`       `180.0` +> This sets the time limit of the fitting procedure in seconds. The algorithm is stopped, if the next chain would be completed after +> the time limit. This parameter should be adapted to the computing time limit of the system Emerge is running on, taking into account +> the time the code needs to set up everything before the fitting starts. In this way, the code can be terminated before the load +> managing system stops it while running. + +`MCMCseed`       `12345` +> This integer value sets the seed for the fitting algorithm. For the same seed value, the algorithm will produce the same walker chains. +> If the code is run to start a new fitting procedure, a file containing the seed value and a `000` ending will be created, e.g. +> `mcmc12345.000.out`. If this file already exists, it will be overridden. If fitting is resumed with `1` added to the flag behind the +> parameter file (e.g. 11 to resume the MCMC fitting), a new file will be created with an incremented number. For example, if the fitting +> is resumed for the first time, the file `mcmc12345.001.out`, will be created. If the fitting is restarted again, the file +> `mcmc12345.002.out` will be created, and so on. Changing the seed value will result in completely different chains, such that several +> ensembles of walkers with different seeds can be run at the same time. + + +#### 4.11) Output parameters + +`NumOutputFiles`       `1` +> This sets the number of files each output product is split into. This applies to galaxy and catalogues at a given redshift, while +> catalogues at different redshifts will be already separated into different files. + +`OutputFormat`       `1` +> This parameter indicates which output format will be used. A value of 1 will produce output in ASCII format, while for a value of +> 2, the HDF5 format is used. For more details on the specific structure of the output files, see [Output files](#7--output-files) . + +`OutputRedshifts`       `0.1,0.5,1.0` +> This comma-separated list specifies the output redshifts for which galaxy and halo catalogues are created. If only a single value +> is given, only one output catalogue at this redshift will be written. If several values are given, separated by commas, output files +> will be written for each redshift. Note that there must not be any space between values and commas. + +`OutputMassThreshold`       `7.0` +> This sets the minimum stellar mass for which a system is included in the output catalogues. The value is given in logarithmic solar +> masses. + +`MainBranchMasses`       `10.0,11.0,12.0` +> This sets the mean of the mass bin for which galaxy main branches will be written. The parameter `MainBranchMassType` controls if +> these are given in halo masses or stellar masses. If only a single value is given, only one mass bin will be written. If several +> values are given, separated by commas, each mass bin will be written. Note that there must not be any space between values and commas. + +`MainBranchBinSize`       `0.1,0.2,0.5` +> This sets the mass bin width for which main branches will be written. The number of values given in this list must be the same as +> for the `MainBranchMasses` list. + +`MainBranchMassType`       `1` +> This defines which mass will be used for the selection. For a `0` the mass bin corresponds to the halo mass, while for `1` the mass +> bin corresponds to the (intrinsic) stellar mass. + +`MainBranchRedshift`       `0.0` +> This value sets the redshift at which the systems are selected, i.e. they must have a (halo or stellar) mass that falls into the +> specified bins at this redshift. + + +## 5) Observed data files + +In Emerge the properties of simulated dark matter haloes are linked to the observed properties of galaxies. This is achieved by +using global statistics, such as the stellar mass function and galaxy clustering. Each time a model with a set of parameters is +run, mock observations are computed and compared to observed data. This data is stored in ASCII format in several files in the +`data` folder. To fit the model to different data sets, these files can be simply modified to suit any configuration. Typically, +they contain blocks of individual data points representing data sets reported by different publications. The following +sections provide an overview of the different statistical data, the format of the files, and how to modify them. + + +#### 5.1) Stellar Mass Functions + +The stellar mass functions are stored in the `data/smf.dat` file, and are read and used in the fit only if the option +`READ_SMF` has been set. This file starts with a general header line and is followed by several blocks of data, which in turn +consist of a header line and individual data. The general header starts with a `#` and then lists the entries that each data block +header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data points in +this block, the minimum and the maximum redshift of the stellar mass function, a correction value for the (universal) initial mass +function (IMF) of the data, the Hubble parameter, and a string that identifies the publication from which the data is taken. A typical +block header thus has the format + + +`# 39 0.0 0.2 0.0 1.0 Li & White 2009`, + + +which signifies that the data block has 39 entries (excluding this header), ranges from z=0.0 to z=0.2, has no IMF +correction, uses a Hubble parameter of h=1.0, and is from the paper by Li & White (2009). + +The IMF can be normalised to any choice, as long as this choice is consistent for all data files. For example, if the model shall be +calibrated to a Chabrier IMF, the correction for the data blocks that are given for a Chabrier IMF is 0, while for the data blocks that +are given for a Salpeter IMF, the correction value would be 0.24, which is then subtracted from the logarithmic stellar masses. The +block header will then look like `# 19 0.6 1.0 0.24 0.7 Santini 2012`. If instead the masses shall be calibrated to a Salpeter IMF, +data using a Chabrier IMF will need to have a block header with a IMF correction value of -0.24, while data using a Salpeter IMF would +have a correction value of 0. + +Each data block consists of several lines that form the stellar mass function in the given redshift interval. These lines consist of +the logarithmic stellar mass, the logarithm of the number density per dex, the upper 1 σ number density, the lower +1 σ number density, and a weight factor. This factor will be multiplied to the χ2 value of the corresponding +data point. For a complete parameter space exploration it should be set to 1 for each data point. However, for testing purposes it can +be set to zero to ignore some data, or to a larger value to see how the model changes if one observation is emphasised. A typical data +block will look like: + + +`# 39 0.0 0.2 0.0 1.0 Li & White 2009` + +`8.05 -1.216 -1.174 -1.263 1` + +`8.15 -1.312 -1.264 -1.366 1` + +`... [35 more lines] ...` + +`11.45 -4.745 -4.702 -4.794 1` + +`11.55 -5.312 -5.237 -5.403 1` + + +New data blocks can be simply added to the end of the file, taking into account that each block needs a header as described above, and +that the number of data points in the header correspond to the number of lines in that block. Note that there must not be empty lines +before the end of the file. + + +#### 5.2) Quenched Fractions + +The fraction of quenched galaxies as a function of stellar mass are stored in the `data/fq.dat` file, and are read and used in the fit +only if the option `READ_FQ` has been set. This file starts with a general header line and is followed by several blocks of data, which +in turn consist of a header line and individual data. The headers have the same format as in the stellar mass function file. The IMF +can be normalised with a correction value in the same way as for the stellar mass functions. Note that the IMF choice has to be the same +everywhere. + +The data blocks consist of several lines that give the quenched fractions in the given redshift interval. These lines consist of +the logarithmic stellar mass, the fraction of quenched galaxies, the 1 σ uncertainty, and a weight factor that will be +multiplied to the χ2 value of the corresponding data point. A typical data block will look like: + + +`# 32 0.2 0.5 0.03 0.7 Muzzin 2013` + +`8.4 0.142 0.157 1` + +`8.5 0.122 0.100 1` + +`... [28 more lines] ...` + +`11.4 0.943 0.237 1` + +`11.5 0.920 0.285 1` + + +New data blocks can be added to the end of the file in the same way as for the SMFs. + + +#### 5.3) Cosmic Star Formation Rate Density + +The cosmic star formation rate densities are stored in the `data/csfrd.dat` file, and are read and used in the fit only if the option +`READ_CSFRD` has been set. This file starts with a general header line and is followed by several blocks of data, which +in turn consist of a header line and individual data. The general header starts with a `#` and then lists the entries that each data block +header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data points in +this block, the IMF correction value, the Hubble parameter, and a string that identifies the publication from which the data is taken +and the wavelength band that has been used. A typical block header thus has the format + + +`# 1 0.24 1.0 Robotham & Driver 2011 UV`, + + +which signifies that the data block has 1 entry, wil be converted from a Salpeter to a Chabrier IMF, uses a Hubble parameter +of h=1.0, and is from the paper by Robotham & Driver (2011). Note that the IMF choice has to be the same everywhere. + +The data blocks consist of several lines that give the cosmic star formation rate density at several redshifts. These lines consist of +the redshift, the logarithmic cosmic star formation rate density in solar masses per year and cubic Megaparsec, the +upper 1 σ uncertainty in dex, the lower 1 σ uncertainty in dex, and a weight factor that will be +multiplied to the χ2 value of the corresponding data point. A typical data block will look like: + + +`# 4 0.0 0.7 Zheng 2006 UV/IR` + +`0.3 -1.7060 0.1261 0.1821 1` + +`0.5 -1.4575 0.0977 0.1255 1` + +`0.7 -1.2438 0.0710 0.0801 1` + +`0.9 -1.2103 0.0776 0.0944 1` + + +New data blocks can be simply added to the end of the file, taking into account that each block needs a header as described above, the +number of data points in the header corresponds to the number of lines in that block, and there are no empty lines +before the end of the file. + + +#### 5.4) Specific Star Formation Rates + +The specific star formation rates are stored in the `data/ssfr.dat` file, and are read and used in the fit only if the option +`READ_SSFR` has been set. This file starts with a general header line and is followed by several blocks of data, which +in turn consist of a header line and individual data. The headers have the same format as in the cosmic star formation rate density +file. The IMF can be normalised with a correction value, but the IMF choice has to be the same everywhere. + +The data blocks consist of several lines that give the specific star formation rate as a function of stellar mass and redshift. +These lines consist of the redshift, logarithmic stellar mass, the logarithm of the specific star formation rate in yr-1, +the upper 1 σ uncertainty in dex, the lower 1 σ uncertainty in dex, and a weight factor that will be +multiplied to the χ2 value of the corresponding data point. A typical data block will look like: + + +`# 60 0.0 0.7 Whitaker 2012` + +`0.25 8.58 -9.31 0.3 0.3 1` + +`0.25 8.69 -9.29 0.3 0.3 1` + +`... [56 more lines] ...` + +`2.25 11.00 -8.78 0.3 0.3 1` + +`2.25 11.25 -8.90 0.3 0.3 1` + + +New data blocks can be simply be added to the end of the file, although there must not be empty lines before the end of the file. + + +#### 5.5) Projected correlation functions + +The projected galaxy auto-correltion function are stored in the `data/wp.dat` file, and are read and used in the fit only if the option +`READ_WP` has been set. In the current implementation, all observed correlation functions must be at the same redshift, which is +specified at the beginning of the file. The following line is a general header that starts with a `#` and then lists the entries that +each data block header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data +points in this block, the minimum logarithmic stellar mass for this bock, the maximum logarithmic stellar mass for this block, the +maximum line-of-sight projection distance πmax, the IMF correction value, the Hubble parameter, and a string that +identifies the publication from which the data is taken. A typical block header thus has the format + + +`# 25 9.0 9.5 10.0 0.0 1.0 Li 2006`, + + +which signifies that the data block has 25 entries (excluding this header), ranges from log m* = 9.0 to log m* = 9.5, +has a projection distance of πmax = 10 Mpc/h, no IMF correction, uses a Hubble parameter of h=1.0, +and is from the paper by Li et al. (2006). + +Each data block consists of several lines that give the projected correlation function in the given stellar mass bin. These lines consist +of the projected distance in Mpc/h, the projected correlation function in Mpc/h, the 1 σ +uncertainty, and a weight factor that will be multiplied to the χ2 value of the corresponding data point. A typical data block +will look like: + + +`# 25 9.0 9.5 10.0 0.0 1.0 Li 2006` + +`0.11 411.3 133.7 1` + +`0.14 428.6 94.3 1` + +`... [21 more lines] ...` + +`22.54 0.9 1.8 1` + +`28.37 0.5 1.8 1` + + +New data blocks can be simply be added to the end of the file. Note that there must not be empty lines before the end of the file, and +the redshift for all correlation functions must be the same (given at the start of the file). + + +## 6) Halo merger tree files + +In addition to observed galaxy data, Emerge relies on simulated dark matter halo merger trees that will be populated with galaxies. The +files that containt these merger trees and their locations can be specified in the parameter file with the parameter `TreefileName`. The +suggested location for the merger tree files is the `trees` folder. If the trees are located in a single file, `TreefileName` corresponds +to the name of this file including folder, e.g. `trees/P100`. If the trees are stored within multiple files, `TreefileName` gives the file +base name, which will be appended with the file index, e.g. `trees/P100` corresponds to the files `P100.0`, `P100.1`, `P100.2`, `...`, +in the `trees` folder. There is no restriction on the number of files that the trees are split into, although an individual tree cannot +be split into different files. The code will automatically detect the number of files. +Merger tree files extracted from a simulation with a side length of 100 Mpc can be downloaded from the code website +http://www.usm.lmu.de/emerge. + + + +Each merger tree file has the same binary format. It starts with header giving the basic information contained in the file, which is +followed by the properties for each halo. The files use the common `unformatted binary' convention, for which the data of each read or +write statement is bracketed by fields that give the length of the data block in bytes. One such 4-byte integer field is stored before and +after each data block. This is useful to check the consistency of the file structure, to make sure that the data is read at the correct +place, and to skip individual blocks quickly by using the length information of the block size fields to fast forward in the file without +actually having to read the data. The sequence of blocks in a file has the following format: + + #  | Block name | Type | Bit size | Elements | Comment +:-----------: | ---------- | ------ | :--------: | :---------: | -------- +0 | `Ntrees` | int | 32 | 1 | Number of merger trees in this file +1 | `Nhalos` | int | 32 | `Ntrees` | Number of haloes in each merger tree +2 | `TreeID` | IDtype | 32 or 64 | `Ntrees` | ID of each tree +3 | `Halo` | struct | 480 or 576 | `Nhalostot` | Properties of each halo in this file + +The data type `IDtype` is `unsigned int` by default. However, for very large simulations the total number of haloes can be larger than +2 billion. In this case `IDtype` needs to be able to cover this range, and can be set to the type `unsigned long long int` if the `LONGIDS` +option is enabled. In total the `Halo` block contains `Nhalostot = Sum_i Nhalos_i` elements, i.e. the sum of the haloes in all trees. +The halo properties are stored in the form of a structure for each halo. This structure has the following format: + + #  | Block name | Type | Bit size | Example | Comment +:-----------: | ---------- | -------------- | :--------: | ------------- | -------- +0 | `haloid` | IDtype | 32 or 64 | 123456788 | ID of the halo (unique across simulation and snapshots) +1 | `descid` | IDtype | 32 or 64 | 123456789 | ID of the halo's descendant in the next snapshot +2 | `upid` | IDtype | 32 or 64 | 223456788 | ID of the most massive host halo +3 | `np` | unsigned short | 16 | 5 | Number of progenitors in the previous snapshot +4 | `mmp` | unsigned short | 16 | 1 | Flag indicating if the halo is the most massive progenitor (1) or not (0) +5 | `scale` | float | 32 | 0.99 | Scale factor a of the halo +6 | `mvir` | float | 32 | 1.1e12 | Virial mass in M/h +7 | `rvir` | float | 32 | 220.5 | Virial radius in kpc/h +8 | `c` | float | 32 | 11.5 | Halo concentration (Rvir/Rs) +9 | `lambda` | float | 32 | 0.033 | Halo spin parameter +10 | `pos[3]` | float (x3) | 96 | 3.1, 2.4, 7.1 | Halo position in Mpc/h (x,y,z) +11 | `vel[3]` | float (x3) | 96 | 302, -19, 212 | Halo velocity in km/s (vx,vy,vz) + +The halo IDs may not be smaller than 1. If a halo has no descendant (e.g. for z=0), the descendant ID `descid` should be 0. Similarly, +if a halo is a host halo, the ID of the most massive host halo `upid` should be 0. Both the position and the velocity are stored as +3D arrays. + +For some applications, for example to compute the amount of stellar mass in the ICM, it is necessary that the trees are ordered within +forests, such that all merger trees that reference the same host ID need to be grouped together. In practice, this is achieved by providing +a separate file in the same folder as the tree files, which lists for each tree ID (left column) the corresponding forest ID (right column). +The name of the file has to consist of the tree file base name and a `.forests` extension, e.g. `P100.forests`. + +While merger trees that have been created with any software can easily be converted to this format, a code that converts merger trees created +with the Rockstar / consistent-trees software into the Emerge format is provided in the `tools` folder. Compiling Emerge should automatically +also compile the `convert_CT_to_emerge.c` file and create the `convert_CT_to_emerge.c` executable, which converts a single ASCII tree file +created with consistent-trees. Before compiling, it should be verified that the column numbers specified at the beginning of this file correspond +to the correct halo properties for the adopted version of consistent-trees. The executable must be called with the input file and output file +as arguments. If consistent-trees has divided the merger trees into several files (boxes) with the names `tree_0_0_0.dat`, `tree_0_0_1.dat`, +`...`, the shell script `consistent_trees_to_emerge.sh` can convert all files to the appropriate Emerge files. It needs to be called with the +output base name and the number of box divisions as arguments. The command `consistent_trees_to_emerge.sh P100 2` converts all 8 files from +`tree_0_0_0.dat` to `tree_1_1_1.dat` into the files `P100.0` to `P100.7`. + + +## 7) Output files + +At the end of each run, Emerge can write different data products to files. This includes the model predictions for the statistics that +have been used in the fit, mock galaxy catalogues, halo catalogues, and the main branch evolution of each system. In future releases, +Emerge will also be able to write the full galaxy merger trees, and support lightcone output. All files can either be written in standard +ASCII format, or, if the option `HDF5_SUPPORT` has been set and the proper libraries have been installed, the HDF5 format can be used. +The output format can be selected before each run in the parameter file with the `OutputFormat` parameter. All files will be written to +the `output` folder, within the subfolder corresponding to the parameter `ModelName`. Emerge supports parallel output by distributing +the catalogues into several files, each written by a group of processors, which may simplify the handling of very large simulations. + + +#### 7.1) Global statistics + +The global statistics that have been used to fit the model will be written to individual files in the `statistics` subfolder for ASCII +output, and to individual datasets within the file `statistics.h5` for HDF5 outout. For each seperate universe that has been run in +parallel as specified by the `UniversesInParallel` parameter, separate statistics will be written. For the ASCII format, the index of +the universe is given in the file names, so that the `*.0.out` files correspond to the first universe. For the HDF5 output, the +`statistics.h5` file contains a group that holds all data for each universe. + +##### 7.1.1) Stellar Mass Functions + +For the ASCII format, the observed stellar mass functions, converted to the globally adopted IMF and Hubble parameter, are stored in +the `smfobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by +empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the redshift bin +edges and the publication reference. Each line in the data block contains the logarithmic stellar mass, the observed logarithmic number +density, the observational uncertainty, and the model prediction for this data point. The `smfmod.*.out` files contain the model stellar +mass function at each redshift for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar +mass bin followed by a model value for the stellar mass function at each redshift. + +For the HDF5 format, the stellar mass functions are stored in the `SMF` group. The `Sets` HDF5 compound gives an overview of the +different data sets. It has the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------- | ------ | :------: | ----------- | ------- +0 | `Ndata` | int | 32 | 37 | The number of data points in this data set +1 | `Offset` | int | 32 | 604 | The offset with respect to the first data set +2 | `Redshift_min` | float | 32 | 0.2 | The minimum redshift +3 | `Redshift_max` | float | 32 | 0.5 | The maximum redshift +4 | `Tag` | string | 400 | Muzzin 2013 | A tag referencing the publication + +All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of +each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ------------------ | ----- | :------: | ------- | ------- +0 | `Stellar_mass` | float | 32 | 8.39761 | The logarithmic stellar mass in solar masses +1 | `Phi_observed` | float | 32 | -1.6404 | The logarithm of the observed number of galaxies per cubic Megaparsec and dex +2 | `Sigma_observed` | float | 32 | 0.31167 | The observational uncertainty in dex +3 | `Mean_ScaleFactor` | float | 32 | 0.75 | The mean scale factor a of the redshift bin +4 | `Phi_model` | float | 32 | -1.6724 | The logarithm of the model prediction for the number density of galaxies +5 | `Sigma_model` | float | 32 | 0.00661 | The model uncertainty in dex resulting from Poisson noise + +The model stellar mass function at each redshift is stored in the table `Model`. The first row contains the scale factors that correspond +to each column, while the first column contains the logarithmic stellar masses that correspond to each row. The remaining table entries +give the logarithm of the stellar mass function (in dex-1 Mpc-3) at the given stellar mass and scale factor. + + +##### 7.1.2) Quenched Fractions + +For the ASCII format, the observed quenched fractions, converted to the globally adopted IMF and Hubble parameter, are stored in +the `fqobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by +empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the redshift bin +edges and the publication reference. Each line in the data block contains the logarithmic stellar mass, the observed quenched fraction, +the observational uncertainty, and the model prediction for this data point. The `fqmod.*.out` files contain the model quenched +fractions for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar mass +bin followed by a model value for the quenched fraction at each redshift. + +For the HDF5 format, the quenched fractions are stored in the `FQ` group. The `Sets` HDF5 compound gives an overview of the +different data sets. It has the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------- | ------ | :------: | ----------- | ------- +0 | `Ndata` | int | 32 | 32 | The number of data points in this data set +1 | `Offset` | int | 32 | 266 | The offset with respect to the first data set +2 | `Redshift_min` | float | 32 | 0.2 | The minimum redshift +3 | `Redshift_max` | float | 32 | 0.5 | The maximum redshift +4 | `Tag` | string | 400 | Muzzin 2013 | A tag referencing the publication + +All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of +each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ------------------ | ----- | :------: | ------- | ------- +0 | `Stellar_mass` | float | 32 | 10.5976 | The logarithmic stellar mass in solar masses +1 | `Fq_observed` | float | 32 | 0.50100 | The observed fraction of quenched galaxies +2 | `Sigma_observed` | float | 32 | 0.11413 | The observational uncertainty +3 | `Mean_ScaleFactor` | float | 32 | 0.75 | The mean scale factor a of the redshift bin +4 | `Fq_model` | float | 32 | 0.47875 | The model prediction for the fraction of quenched galaxies +5 | `Sigma_model` | float | 32 | 0.01463 | The model uncertainty resulting from Poisson noise + +The model quenched fractions at each redshift are stored in the table `Model`. The first row contains the scale factors that correspond +to each column, while the first column contains the logarithmic stellar masses that correspond to each row. The remaining table entries +give the fraction of quenched galaxies at the given stellar mass and scale factor. + + +##### 7.1.3) Cosmic Star Formation Rate Density + +For the ASCII format, the observed cosmic star formation rate densities, converted to the globally adopted IMF and Hubble parameter, are +stored in the `csfrdobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated +by empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points and the publication +reference. Each line in the data block contains the redshift, the observed logarithmic cosmic star formation rate density in solar masses per +year and cubic Megaparsec, the observational uncertainty, and the model prediction for this data point. The `csfrdmod.*.out` files contain the +redshift (first column) and the corresponding model cosmic star formation rate density (second column). + +For the HDF5 format, the cosmic star formation rate densities are stored in the `CSFRD` group. The `Sets` HDF5 compound gives an overview of the +data sets. It has the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------- | ------ | :------: | -------------- | ------- +0 | `Ndata` | int | 32 | 4 | The number of data points in this data set +1 | `Offset` | int | 32 | 106 | The offset with respect to the first data set +2 | `Tag` | string | 400 | Duncan 2014 UV | A tag referencing the publication + +All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of +each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ---------------- | ----- | :------: | ------- | ------- +0 | `Redshift` | float | 32 | 4.0 | The redshift +1 | `Csfrd_observed` | float | 32 | -1.4438 | The observed cosmic star formation rate density in solar masses per year and cubic Megaparsec +2 | `Sigma_observed` | float | 32 | 0.12806 | The observational uncertainty +3 | `Csfrd_model` | float | 32 | -0.9831 | The model prediction for the cosmic star formation rate density + +The model predictions are stored in the table `Model`. The first column contains the redshift, and the second column contains the corresponding +cosmic star formation rate density. + + +##### 7.1.4) Specific Star Formation Rates + +For the ASCII format, the observed specific star formation rates, converted to the globally adopted IMF and Hubble parameter, are stored in +the `ssfrobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by +empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points and the publication +reference. Each line in the data block contains the redshift, the logarithmic stellar mass, the observed specific star formation rate, +the observational uncertainty, and the model prediction for this data point. The `ssfrmod.*.out` files contain the model specific star +formation rates at each redshift for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar +mass bin followed by a model value for the specific star formation rate at each redshift. + +For the HDF5 format, the stellar mass functions are stored in the `SSFR` group. The `Sets` HDF5 compound gives an overview of the +different data sets. It has the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------- | ------ | :------: | ----------- | ------- +0 | `Ndata` | int | 32 | 46 | The number of data points in this data set +1 | `Offset` | int | 32 | 24 | The offset with respect to the first data set +2 | `Tag` | string | 400 | Noeske 2007 | A tag referencing the publication + +All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of +each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ---------------- | ----- | :------: | ------- | ------- +0 | `Redshift` | float | 32 | 0.775 | The redshift +1 | `Ssfr_observed` | float | 32 | -9.480 | The logarithm of the observed specific star formation rate in yr-1 +2 | `Sigma_observed` | float | 32 | 0.335 | The observational uncertainty in dex +3 | `Stellar_mass` | float | 32 | 10.397 | The logarithmic stellar mass in solar masses +4 | `Ssfr_model` | float | 32 | -9.597 | The logarithm of the model prediction for the specific star formation rate +5 | `Sigma_model` | float | 32 | 0.012 | The model uncertainty in dex resulting from Poisson noise + +The model specific star formation rates at each stellar mass and redshift are stored in the table `Model`. The first row contains the logarithmic +stellar masses that correspond to each column, while the first column contains the redshifts that correspond to each row. The remaining table entries +give the logarithm of the specific star formation rate (in yr-1) at the given stellar mass and scale factor. + + +##### 7.1.5) Galaxy Clustering + +For the ASCII format, the observed projected correlation functions, converted to the globally adopted IMF and Hubble parameter, are stored in +the `ssfrobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by +empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the stellar mass bin edges, +maximum line-of-sight projection distance πmax, and the publication reference. Each line in the data block contains the +projected distance, the observed projected correlation function, the observational uncertainty, the model prediction for this data point, and +the model error, based on Poisson noise. The `ximod.*.out` files contain the model predictions for the 3D correlation functions in the same +stellar mass bins as the observations. The first line starts with a `#` followed by all stellar mass bins. The subsequent lines contain the +3D distance, the model 3D correlation function, and the model uncertainty for each stellar mass bin. + +For the HDF5 format, the clustering data are stored in the `Clustering` group. The `Sets` HDF5 compound gives an overview of the +different data sets. It has the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------- | ------ | :------: | ------- | ------- +0 | `Ndata` | int | 32 | 27 | The number of data points in this data set +1 | `Offset` | int | 32 | 50 | The offset with respect to the first data set +2 | `Minimum_Mass` | float | 32 | 10.3374 | The minimum logarithmic stellar mass +3 | `Maximum_Mass` | float | 32 | 10.8374 | The maximum logarithmic stellar mass +4 | `Pi_max` | float | 32 | 14.7471 | The maximum projected distance in Megaparsecs +5 | `Tag` | string | 400 | Li 2006 | A tag referencing the publication + +All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of +each compound reflects the publication reference and the stellar mass bins. The observational data compounds have the following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ---------------- | ----- | :------: | ------- | ------- +0 | `Radius` | float | 32 | 0.3391 | The projected radius in Megaparsecs +1 | `Wp_observed` | float | 32 | 322.66 | The projected galaxy auto-correltion function in Megaparsecs +2 | `Sigma_observed` | float | 32 | 135.46 | The observational uncertainty in Megaparsecs +3 | `Wp_model` | float | 32 | 303.63 | The model prediction for the projected galaxy auto-correltion function +4 | `Sigma_model` | float | 32 | 16.79 | The model uncertainty in Megaparsecs resulting from Poisson noise + +The model 3D auto-correlation functions for each stellar mass bin are stored in the `Model_3D` subgroup, and are organised in a HDF5 compound +for each data set. The name of each compound reflects the publication reference and the stellar mass bins. The data compounds have the +following structure: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ---------- | ----- | :------: | ------- | ------- +0 | `Radius` | float | 32 | 1.0015 | The 3D radius in Megaparsecs +1 | `Xi` | float | 32 | 41.049 | The 3D galaxy auto-correltion function +2 | `Sigma_xi` | float | 32 | 1.9703 | The model uncertainty + + +##### 7.1.6) Model likelihoods + +To check how well the model fits the observed data, Emerge writes out the calculated χ2 values. For the ASCII output, the values +are stored in the `chi2.out` file. The header line starts with a `#` and then lists all global statistics that have been used in the fit. The +following lines then list the χ2 values for each universe. Each line starts with the universe index, and then gives the +total χ2 value, followed by the χ2 values for the individual statistics. For the HDF5 output, the χ2 values +are stored in the `Chi2` table in the `statistics.h5` file for each universe. + + +##### 7.1.7) Adopted model parameters + +The model parameters that have been used to run each universe are stored for the HDF5 output format in the `statistics.h5` file. The HDF5 +compound `Model_Parameters` within each universe group contains all parameters as double (64 bit) values. + + +#### 7.2) Galaxy Catalogues + +If Emerge is not called with a flag behind the parameter file, which corresponds to the code mode 0, mock galaxy catalogue are written out +as long as the option `WRITE_GALAXY_CATALOG` has been set. The output format (ASCII or HDF5) can be chosen with the parameter `OutputFormat` +in the parameter file. For each output redshift that has been specified with the parameter `OutputRedshifts`, a file containing all galaxies +in the simulation box that are more massive than the threshold `OutputMassThreshold` will be written written. The name of each file begins +with `galaxies.S`, followed by the snapshot number that corresponds to its redshift, and a file extension, e.g. `galaxies.S90.out` for +snapshot 90 and ASCII format. Note that the mock catalogue is only written for the first universe if `UniversesInParallel` is larger than 1. + +All ASCII files have a header starting with a `#` which lists the contents of each column, as well as several key parameters that have been +chosen to create the catalogue. The table following this header then contains the galaxy and halo properties of one system in each line. The +HDF5 files contain a compound dataset that lists the properties of each system. The key parameters are stored as attributes of this dataset: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | ---------------------- | ------ | :------: | ------- | ------- +0 | `Scale Factor` | float | 32 | 1.0 | Scale factor of the catalogue +1 | `Box Size` | double | 64 | 100.0 | Box side length in Mpc +2 | `Hubble Parameter` | double | 64 | 0.6781 | Hubble parameter h = H / (100 kms-1Mpc-1) +3 | `Omega_0` | double | 64 | 0.308 | Matter density parameter. +4 | `Omega_Lambda` | double | 64 | 0.692 | Dark energy density parameter +5 | `Omega_Baryon_0` | double | 64 | 0.0484 | Baryon density parameter +6 | `Minimum Stellar Mass` | double | 64 | 7.0 | Logarithm of the minimum stellar mass in M + +Each system in the dataset has several properties: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------------- | -------------- | :------: | ------- | ------- +0 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log M +1 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in M yr-1 +2 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log M +3 | `Halo_mass_host` | float | 32 | 12.41 | Virial mass of the host halo in log M +4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached +5 | `Scale_half_mass` | float | 32 | 0.69 | Scale factor when half of the current virial mass was reached +6 | `Halo_radius` | float | 32 | 373.81 | Virial radius in kpc +7 | `Concentration` | float | 32 | 4.41 | Concentration parameter (c = Rvir/Rs) +8 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \lambda +9 | `Stellar_mass` | float | 32 | 10.52 | Stellar mass of the galaxy in log M +10 | `SFR` | float | 32 | 0.72 | Star formation rate of the galaxy in M yr-1 +11 | `Intra_cluster_mass` | float | 32 | 9.051 | Stellar mass of the halo (excluding satellites) in log M +12 | `Stellar_mass_obs` | float | 32 | 10.71 | Observed stellar mass of the galaxy in log M +13 | `SFR_obs` | float | 32 | 0.59 | Observed star formation rate of the galaxy in M yr-1 +14 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in Mpc +15 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in Mpc +16 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in Mpc +17 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in kms-1 +18 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in kms-1 +19 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in kms-1 +20 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) +21 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo +22 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) +23 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + +If the parameter `NumOutputFiles` is set to a number larger than 1, each mock catalogue at a fixed redshift will be split into several files. The +file index will be appended to the filename before the file extension, e.g. `galaxies.S90.0.h5`, `galaxies.S90.1.h5`, `...`. + + +#### 7.3) Halo Catalogues + +Similar to the galaxy catalogue output, Emerge can create halo catalogues if called without a flag (code mode 0), and the option `WRITE_HALO_CATALOG` +has been set. These halo catalogues include the subhaloes that correspond to orphan galaxies, which can be useful if the catalogues are used to +link galaxies to haloes, e.g. for subhalo abundance matching. The output format (ASCII or HDF5) is again chosen with the parameter `OutputFormat` +and the output redshifts are specified with `OutputRedshifts`. The files start with `haloes.S`, followed by the snapshot number and a file extension, +e.g. `haloes.S90.h5`. The mock catalogue is again only written for the first universe. The ASCII files have a similar header as the galaxy catalogue +files, and are followed by a table that lists the properties of one halo in each line. For the HDF5 files, the halo properties are stored in a +compound dataset, with key parameters stored as attributes. The following halo properties are written: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------------- | -------------- | :------: | ------- | ------- +0 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log M +1 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in M yr-1 +2 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log M +3 | `Halo_mass_host` | float | 32 | 12.41 | Virial mass of the host halo in log M +4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached +5 | `Scale_half_mass` | float | 32 | 0.69 | Scale factor when half of the current virial mass was reached +6 | `Halo_radius` | float | 32 | 373.81 | Virial radius in kpc +7 | `Concentration` | float | 32 | 4.41 | Concentration parameter (c = Rvir/Rs) +8 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \lambda +9 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in Mpc +10 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in Mpc +11 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in Mpc +12 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in kms-1 +13 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in kms-1 +14 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in kms-1 +15 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) +16 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo +17 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) +18 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + +To generate halo catalogues with haloes that correspond to orphan galaxies, it is recommended to set the normalisation of the conversion +efficiency (ε0 and εz), and the stripping parameter fs to zero. In this case, the stellar +mass in each halo will be zero, and the dynamical friction times will be based only on the halo mass. + + +#### 7.4) Main Branches + +To track galaxies and haloes through cosmic time, Emerge can write the properties of systems on the main branch of the merger tree. For this, +Emerge must be called without a flag (code mode 0), and the option `WRITE_MAINBRANCH` has to be set. The output format (ASCII or HDF5) is chosen +with the parameter `OutputFormat`. Galaxies are selected at a specific redshift with `MainBranchRedshift`, and in several mass bins with +`MainBranchMasses` and `MainBranchBinSize`, while `MainBranchMassType` specifies which mass is used for this selection (0: halo mass, 1: stellar +mass). For each galaxy that fulfils this criterion, all main progenitors and descendants are written as a data block. The file name starts with +`mainbranches.S`, followed by the snapshot number that corresponds the selection redshift, and a file extension, e.g. `mainbranches.S90.out` for +the main branches that have been selected to fulfil the mass criterion at snapshot 90. + +The ASCII files have a header block starting with a `#`, which lists the contents of each column, as well as several key parameters, such as the +selection mass bins and the box size. After the header block, each main branch is written as a data block, separated by empty lines. Each data +block starts with an index given by the mass bin and the main branch number, e.g. `#000_0000000` corresponds to the first mass bin and the first +main branch in that bin. After this index, a table lists the properties of each galaxy on the main branch of the merger tree. + +The HDF5 files include an attribute `Selection_Redshift` listing the redshift for which galaxies have been selected. The main branches are stored +within HDF5 groups for each mass bin, e.g. `MainBranch_M000` for the first mass bin. Each of these groups has the following attributes: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | --------------- | ------ | :------: | -------------- | ------- +0 | `Mass_bin` | double | 64 | 10.0 | Selection mass in log M +1 | `Mass_bin_size` | double | 64 | 0.1 | Selection mass bin size in dex +2 | `Mass_Type` | string | 72 or 96 | `Stellar_Mass` | Which mass was used in the selection (Halo_Mass or Stellar_Mass) + +The main branches are written as HDF5 compound datasets in each mass bin group. The name of each dataset stars with `Tree_` followed by the Index +of the main branch. Each system in the main branch has the following properties: + + #  | HDF5 name | Type | Bit size | Example | Comment +:-----------: | -------------------- | -------------- | :------: | ------- | ------- +0 | `Scale_factor | float | 32 | 1.00 | Scale factor a of the system +1 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log M +2 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in M yr-1 +3 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log M +4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached +5 | `Halo_radius` | float | 32 | 373.81 | Virial radius in kpc +6 | `Concentration` | float | 32 | 4.41 | Concentration parameter (c = Rvir/Rs) +7 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \lambda +8 | `Stellar_mass` | float | 32 | 10.52 | Stellar mass of the galaxy in log M +9 | `SFR` | float | 32 | 0.72 | Star formation rate of the galaxy in M yr-1 +10 | `Intra_cluster_mass` | float | 32 | 9.051 | Stellar mass of the halo (excluding satellites) in log M +11 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in Mpc +12 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in Mpc +13 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in Mpc +14 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in kms-1 +15 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in kms-1 +16 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in kms-1 +17 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) +18 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo +19 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) +20 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + +A single dataset always traces a single system from the root at z=0 up to its leaf at high redshift, i.e. each scale factor is listed only +once. The output allways follows the main progenitor in halo mass, i.e. the most massive progenitor halo is used. + + +## 8) Disclaimer + +The performance and accuracy of Emerge strongly depend on a number of variables, such as the N-body simulations and the derived +halo merger trees, the observational data used to fit the model, and the code parameters. Emerge comes without any warranty, and without any +guarantee that it produces correct results, although the resulting galaxies will undoubtedly be the most elaborate on the market in any case. +The numerical parameter values used in the provided parameter file and in this manual do not represent a specific recommendation by the author, +but are merely a combination that has been found to work for a particular setup. Exploring parameter space is a difficult task, and it is never +guaranteed that an algorithm will converge towards the global maximum of the likelihood function. The user is therefore encouraged to test +different parameters to achieve sufficient accuracy for any given setup. If in doubt about some aspect of Emerge, a sensible strategy is to read +the source code and the scientific paper to understand the details. + + diff --git a/Template-Config.sh b/Template-Config.sh new file mode 100644 index 0000000..770baa9 --- /dev/null +++ b/Template-Config.sh @@ -0,0 +1,73 @@ +#################################################################################################### +#!/bin/bash # this line only there to enable syntax highlighting in this file +#################################################################################################### +# EMERGE Config file - Enable/Disable compile-time options as needed # +#################################################################################################### +# +# +# -------------------------------------------------------------------------------------------------- +# CODE OPTIONS +# -------------------------------------------------------------------------------------------------- +#OPENMPTHREADS=2 # Enables OpenMP support and sets the number of threads per task +RANDOM_NUMBER_TABLE=1000000 # Enables the random number table and sets its size +#LONGIDS # Use 64 bit integers for IDs (default is 32 bit) for large runs +#DISABLE_MEMORY_MANAGER # Disables the default memory manager +# +# -------------------------------------------------------------------------------------------------- +# MODEL OPTIONS +# -------------------------------------------------------------------------------------------------- +SFE_MPEAK_ZEVOLV # Use redshift evolution for M1 +SFE_NORM_ZEVOLV # Use redshift evolution for Epsilon +SFE_BETA_ZEVOLV # Use redshift evolution for Beta +#SFE_GAMMA_ZEVOLV # Use redshift evolution for Gamma +#SFE_SAME_SLOPE # Use the same slope for Beta and Gamma +SAT_QUENCH_MASS_DEPENDENT # Use mass dependent quenching timescale for sats +#SAT_STRIP_USE_MSTAR # Use fraction of mstar in stripping +#SAT_SFR_EXP_DECAY # Exponential decline in str after peak mass +#COMPUTE_ICM # Compute the ICM for each halo +DF_USE_BK08 # Use the dynamical friction fitting function by Boylan-Kolchin (2008) +ORPHAN_MASSLOSS # Let the halo mass of orphans decay on merging timescale +#ORPHAN_NONRADIAL_INFALL # Recompute the position angles of orphans at each time step +# +# -------------------------------------------------------------------------------------------------- +# DATA OPTIONS +# -------------------------------------------------------------------------------------------------- +READ_SMF # Read the stellar mass functions and use in fit +READ_FQ # Read the quenched fractions and use in fit +READ_CSFRD # Read the cosmic SFR density and use in fit +READ_SSFR # Read the specific SFRs and use in fit +READ_WP # Read the projected correlation functions and use in fit +GLOBAL_SIGMA_SMF # Specify a global error for the SMF and add it +GLOBAL_SIGMA_FQ # Specify a global error for the FQ and add it +GLOBAL_SIGMA_CSFRD # Specify a global error for the CSFRD and add it +GLOBAL_SIGMA_SSFR # Specify a global error for the SSFR and add it +GLOBAL_SIGMA_WP # Specify a global error for the WP and add it +# +# -------------------------------------------------------------------------------------------------- +# CORRELATION FUNCTION OPTIONS +# -------------------------------------------------------------------------------------------------- +#WP_RBINS=20 # Number of bins for which the 3d correlation function is computed +#WP_RBINS_INT=1000 # Number of interpolation bins for the 3d correlation function +#WP_RMAX=0.1 # Maximum radius (fraction of box size) of the correlation function +#WP_NLEAF_MIN=4 # Minimum number of objects per kd-node (if more the node is split) +#WP_NODE_WIDTH_MIN=0.01 # Minimum kd-node size (fraction of box size - no more splitting) +# +# -------------------------------------------------------------------------------------------------- +# FIT OPTIONS +# -------------------------------------------------------------------------------------------------- +#HYBRID_ALPHA=0.4 # Hybrid optimization: slope for function g +#HYBRID_BETA=1.0 # Hybrid optimization: slope for function f (larger chi^2) +#HYBRID_GAMMA=1.0 # Hybrid optimization: slope for function f (smaller chi^2) +#PT_COLD_FRACTION=0.25 # Parallel Tempering: Fraction of cold walkers (T=1) +#PT_TARGET_ACCEPTANCE=0.3 # Parallel Tempering: Target acceptance rate +#PT_STEPS_SCALE_ADJUST=10 # Parallel Tempering: Chain steps for adjusting scale +# +# -------------------------------------------------------------------------------------------------- +# OUTPUT OPTIONS +# -------------------------------------------------------------------------------------------------- +HDF5_SUPPORT # Enables HDF5 support for the output files (Use OutputFormat = 2) +WRITE_GALAXY_CATALOG # Writes galaxy catalogues at the specified redshifts +#WRITE_HALO_CATALOG # Writes halo catalogues at the specified redshifts +#WRITE_MAINBRANCH # Writes main branch history of specified systems +# +#################################################################################################### diff --git a/Template-TypeOfSystem.sh b/Template-TypeOfSystem.sh new file mode 100644 index 0000000..f822000 --- /dev/null +++ b/Template-TypeOfSystem.sh @@ -0,0 +1,17 @@ +#################################################################################################### +#!/bin/bash # this line only there to enable syntax highlighting in this file +#################################################################################################### +# EMERGE TypeOfSystem file - Select your Type of Computational System # +#################################################################################################### +# # +# Please copy this file to TypeOfSystem and uncomment your system. # +# Don't commit changes to this file unless you add support for a new system. # +# # +#################################################################################################### + +#TYPEOFSYSTEM="MacBook" +#TYPEOFSYSTEM="Odin" +#TYPEOFSYSTEM="Freya" +#TYPEOFSYSTEM="LRZ" +#TYPEOFSYSTEM="Dorc" +#TYPEOFSYSTEM="C2PAP" diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/compile-config.perl b/compile-config.perl new file mode 100644 index 0000000..20a5102 --- /dev/null +++ b/compile-config.perl @@ -0,0 +1,54 @@ +###################################################################################### +# Emerge code - compile-config.perl # +# Adapted from the GADGET code developed by Volker Springel # +###################################################################################### +# # +# This file processes the configurations options in Config.sh, producing two files: # +# codeoptions.h to be included in each source file (via allvars.h) # +# compile_info.c code to be compiled in, which will print the configuration # +# # +###################################################################################### + +if( @ARGV != 2) +{ + print "usage: perl compile-config.perl \n"; + exit; +} + +open(FILE, @ARGV[0]); +$path = @ARGV[1]; + +open(OUTFILE, ">${path}/codeoptions.h"); +open(COUTF, ">${path}/compile_info.c"); + +print COUTF "#include \n"; +print COUTF "#include \"../src/allvars.h\"\n\n"; +print COUTF "void output_code_options(void)\n\{\n"; + +$count = 0; + +while($line=) +{ + chop $line; + + @fields = split ' ' , $line; + + if(substr($fields[0], 0, 1) ne "#") + { + if(length($fields[0]) > 0) + { + @subfields = split '=', $fields[0]; + + print OUTFILE "#define $subfields[0] $subfields[1]\n"; + print COUTF " printf(\"%c%c%c $fields[0]\\n\",SYMBOL,SYMBOL,SYMBOL);\n"; + $count = $count + 1; + } + } +} + +if ($count<1) +{ + print COUTF " printf(\"%c%c%c NONE\\n\",SYMBOL,SYMBOL,SYMBOL);\n"; +} + +print COUTF "\}\n"; diff --git a/config-makefile b/config-makefile new file mode 100644 index 0000000..a937a2d --- /dev/null +++ b/config-makefile @@ -0,0 +1,6 @@ +RESULT := $(shell mkdir -p $(BUILD_DIR) ) + +all: $(BUILD_DIR)/codeoptions.h + +$(BUILD_DIR)/codeoptions.h: $(CONFIG) + $(PERL) compile-config.perl $(CONFIG) $(BUILD_DIR) diff --git a/data/csfrd.dat b/data/csfrd.dat new file mode 100644 index 0000000..3f8e244 --- /dev/null +++ b/data/csfrd.dat @@ -0,0 +1,144 @@ +# Header: N IMF hubble Source +# 1 0.24 1.0 Robotham & Driver 2011 UV +0.0565 -1.5058 0.0585 0.0676 1 +# 1 0.00 0.7 Salim 2007 UV +0.1 -1.7380 0.0338 0.0094 1 +# 1 0.24 0.7 Ly 2011 Ha +0.8 -1.0 0.22 0.22 1 +# 4 0.0 0.7 Zheng 2006 UV/IR +0.3 -1.7060 0.1261 0.1821 1 +0.5 -1.4575 0.0977 0.1255 1 +0.7 -1.2438 0.0710 0.0801 1 +0.9 -1.2103 0.0776 0.0944 1 +# 7 0.24 0.7 Rujopakarn 2010 FIR +0.025 -1.83 0.08 0.08 1 +0.125 -1.60 0.07 0.07 1 +0.275 -1.43 0.08 0.08 1 +0.425 -1.26 0.08 0.08 1 +0.575 -1.07 0.07 0.07 1 +0.850 -0.92 0.11 0.11 1 +1.150 -0.61 0.14 0.14 1 +# 4 0.24 0.7 Smolcic 2009 1.4GHz +0.225 -1.6383 0.0362 0.0395 1 +0.475 -1.4949 0.0389 0.0280 1 +0.750 -1.3188 0.0263 0.0378 1 +1.100 -1.0555 0.0240 0.0254 1 +# 2 0.24 0.71 Shim 2009 Ha +1.05 -1.0555 0.2138 0.43933 1 +1.65 -0.5768 0.2192 0.46420 1 +# 1 0.24 0.7 Tadaki 2011 Ha +2.2 -0.5086 0.19 0.19 1 +# 4 0.24 0.7 Sobral 2013 Ha +0.40 -1.5229 0.1249 0.1761 1 +0.84 -1.0000 0.0414 0.0969 1 +1.47 -0.8861 0.0621 0.0726 1 +2.23 -0.6778 0.0395 0.0669 1 +# 1 0.24 0.7 Hayes 2010 Ha +2.2 -0.6676 0.1519 0.2355 1 +# 9 0.0 0.7 Karim 2011 1.4Ghz +0.300 -1.7447 0.0458 0.1761 1 +0.500 -1.6383 0.0362 0.1313 1 +0.700 -1.4089 0.0322 0.1139 1 +0.900 -1.2596 0.0155 0.0683 1 +1.100 -1.2147 0.0209 0.0371 1 +1.350 -1.2007 0.0267 0.0512 1 +1.750 -1.0223 0.0597 0.0692 1 +2.250 -1.0088 0.0462 0.0418 1 +2.750 -0.9172 0.0571 0.0658 1 +# 1 0.24 0.7 Ly 2011 UV +2.05 -0.8447 0.0669 0.0792 1 +# 4 0.24 0.7 Kajisawa 2010 UV/IR +0.75 -0.9937 0.13 0.13 1 +1.25 -0.9369 0.17 0.17 1 +2.00 -0.7847 0.19 0.19 1 +3.00 -0.7978 0.27 0.27 1 +# 14 0.24 0.71 Dunne 2009 1.4GHz +0.100 -2.0565 0.2351 0.5663 1 +0.300 -1.5265 0.1344 0.2015 1 +0.500 -1.4114 0.1128 0.1536 1 +0.700 -1.1500 0.0983 0.1272 1 +0.900 -1.1046 0.0912 0.1176 1 +1.100 -0.9463 0.0864 0.1128 1 +1.300 -0.8769 0.0792 0.1055 1 +1.500 -0.6874 0.0744 0.0744 1 +1.700 -0.7499 0.0696 0.0816 1 +1.900 -0.7956 0.0672 0.0767 1 +2.250 -0.9685 0.0504 0.0599 1 +2.760 -0.9758 0.0504 0.0648 1 +3.250 -1.2303 0.0480 0.0600 1 +4.000 -1.7296 0.0576 0.0744 1 +# 10 0.24 0.7 Cucciati 2012 UV +0.125 -1.6500 0.1800 0.1800 1 +0.300 -1.4400 0.1200 0.1200 1 +0.500 -1.3400 0.1000 0.1000 1 +0.700 -1.1500 0.0900 0.0900 1 +0.900 -0.9000 0.0900 0.0800 1 +1.100 -0.8500 0.0900 0.0900 1 +1.450 -0.8500 0.1500 0.0800 1 +2.100 -0.6200 0.4900 0.0900 1 +3.000 -0.8600 0.2600 0.1500 1 +4.000 -1.3700 0.2200 0.3200 1 +# 8 0.24 0.7 Le Borgne 2009 IR/mm +0.05 -1.7520 0.25 0.25 1 +0.50 -1.2048 0.15 0.15 1 +1.00 -1.0159 0.15 0.15 1 +1.50 -0.7951 0.15 0.15 1 +2.00 -0.7390 0.15 0.15 1 +3.00 -0.9881 0.25 0.25 1 +4.00 -1.2125 0.30 0.30 1 +5.00 -1.3736 0.30 0.30 1 +# 3 0.24 0.7 Van der Burg 2010 UV +3.10 -0.8894 0.1750 0.1421 1 +3.80 -1.1079 0.1493 0.1212 1 +4.70 -1.5686 0.1707 0.1303 1 +# 15 0.24 0.7 Oesch 2013 UV +2.50 -0.9300 0.03 0.03 1 +3.80 -1.2120 0.10 0.10 1 +5.00 -1.5381 0.12 0.12 1 +5.90 -1.7173 0.13 0.13 1 +6.80 -1.8883 0.15 0.15 1 +7.40 -2.1300 0.15 0.15 1 +8.00 -2.1980 0.15 0.15 1 +9.00 -2.8516 0.25 0.25 1 +9.00 -2.8101 0.25 0.25 1 +9.20 -2.9821 0.35 0.50 1 +9.60 -2.9163 0.70 0.90 1 +10.7 -2.7192 0.70 0.90 1 +9.80 -3.2596 0.30 0.30 1 +10.0 -3.2595 0.30 0.30 1 +10.0 -3.7502 0.70 0.90 1 +# 5 0.24 0.7 McLure 2013 UV +6.00 -1.8676 0.05 0.05 1 +7.00 -2.0861 0.05 0.05 1 +8.00 -2.2947 0.05 0.05 1 +9.00 -2.6473 0.18 0.18 1 +10.0 -2.9156 0.12 0.12 1 +# 2 0.03 0.7 Reddy & Steidel UV +2.30 -0.8182 0.0969 0.1249 1 +3.05 -0.9586 0.1170 0.1606 1 +# 9 0.24 0.7 Burgarella 2013 UV/IR +0.00 -1.7305 0.1853 0.3300 1 +0.45 -1.2668 0.0413 0.0457 1 +0.90 -0.8827 0.0597 0.0692 1 +1.35 -0.7585 0.0771 0.0939 1 +1.80 -0.7652 0.0816 0.1006 1 +2.25 -0.7001 0.2008 0.3851 1 +2.70 -0.6192 0.2090 0.4179 1 +3.15 -0.7199 0.1873 0.3366 1 +3.60 -0.8914 0.2440 0.6089 1 +# 4 0.0 0.7 Duncan 2014 UV +4.0 -1.43 0.08 0.08 1 +5.0 -1.54 0.13 0.13 1 +6.0 -1.70 0.14 0.14 1 +7.0 -1.90 0.26 0.26 1 +# 4 0.0 0.7 Bourne 2016 UV/IR +1.08 -1.2629 0.0332 0.0332 1 +2.00 -1.1101 0.0260 0.0260 1 +3.19 -1.1492 0.0568 0.0568 1 +4.75 -1.5743 0.0841 0.0841 1 +# 4 0.0 0.7 Gunawardhana 2015 Ha +0.051 -2.05 0.1 0.1 1 +0.125 -2.00 0.1 0.1 1 +0.205 -1.75 0.1 0.1 1 +0.290 -1.72 0.1 0.1 1 +END diff --git a/data/fq.dat b/data/fq.dat new file mode 100644 index 0000000..68222c5 --- /dev/null +++ b/data/fq.dat @@ -0,0 +1,421 @@ +# Header: N zmin zmax IMF Hubble Comment +# 13 0.2 0.4 0.0 0.7 Drory 2009 Cosmos +8.50 0.09666 0.06844 1 +8.75 0.15127 0.06520 1 +9.00 0.16716 0.06547 1 +9.25 0.14023 0.06951 1 +9.50 0.15084 0.07180 1 +9.75 0.17842 0.07302 1 +10.00 0.21915 0.07240 1 +10.25 0.31288 0.06574 1 +10.50 0.39914 0.06291 1 +10.75 0.44560 0.07068 1 +11.00 0.62393 0.07045 1 +11.25 0.69072 0.10242 1 +11.50 0.77778 0.20779 1 +# 11 0.4 0.6 0.0 0.7 Drory 2009 Cosmos +9.00 0.06550 0.07120 1 +9.25 0.07411 0.07188 1 +9.50 0.06964 0.07555 1 +9.75 0.10127 0.07753 1 +10.00 0.12227 0.07795 1 +10.25 0.23546 0.06962 1 +10.50 0.30478 0.06914 1 +10.75 0.38880 0.07417 1 +11.00 0.58726 0.07108 1 +11.25 0.71304 0.09338 1 +11.50 0.82758 0.15715 1 +# 11 0.6 0.8 0.0 0.7 Drory 2009 Cosmos +9.25 0.03065 0.07307 1 +9.50 0.05001 0.07402 1 +9.75 0.06737 0.07653 1 +10.00 0.10965 0.07514 1 +10.25 0.16808 0.07161 1 +10.50 0.25508 0.07001 1 +10.75 0.31241 0.07701 1 +11.00 0.43398 0.08821 1 +11.25 0.61415 0.11108 1 +11.50 0.78313 0.18411 1 +11.75 0.90000 0.31711 1 +# 10 0.8 1.0 0.0 0.7 Drory 2009 Cosmos +9.50 0.01520 0.08146 1 +9.75 0.03748 0.07761 1 +10.00 0.07855 0.07566 1 +10.25 0.13585 0.07181 1 +10.50 0.19342 0.07286 1 +10.75 0.27348 0.07883 1 +11.00 0.38557 0.09281 1 +11.25 0.55343 0.12475 1 +11.50 0.72322 0.23167 1 +11.75 0.71429 0.89080 1 +# 4 0.0 0.1 0.0 0.7 Wetzel 2012 SDSS +9.75 0.23 0.05 1 +10.25 0.45 0.05 1 +10.75 0.72 0.10 1 +11.25 0.90 0.05 1 +# 4 0.4 0.6 0.0 0.7 Wetzel 2012 SDSS +9.75 0.08 0.03 1 +10.25 0.19 0.05 1 +10.75 0.35 0.08 1 +11.25 0.58 0.10 1 +# 4 0.6 0.8 0.0 0.7 Wetzel 2012 SDSS +9.75 0.09 0.03 1 +10.25 0.18 0.05 1 +10.75 0.32 0.10 1 +11.25 0.53 0.05 1 +# 4 0.8 1.0 0.0 0.7 Wetzel 2012 SDSS +9.75 0.06 0.03 1 +10.25 0.13 0.05 1 +10.75 0.26 0.10 1 +11.25 0.48 0.05 1 +# 8 0.2 0.5 0.24 0.7 Lin 2013 PanStarrs +9.24 0.05 0.03 1 +9.55 0.10 0.03 1 +9.84 0.18 0.03 1 +10.14 0.31 0.03 1 +10.44 0.53 0.03 1 +10.74 0.70 0.03 1 +11.04 0.84 0.04 1 +11.33 0.70 0.05 1 +# 8 0.5 0.8 0.24 0.7 Lin 2013 PanStarrs +9.25 0.02 0.03 1 +9.55 0.03 0.03 1 +9.84 0.05 0.03 1 +10.14 0.13 0.03 1 +10.44 0.33 0.03 1 +10.74 0.56 0.03 1 +11.04 0.77 0.03 1 +11.34 0.89 0.03 1 +# 18 0.2 0.5 0.0 0.7 Ilbert 2013 +8.500 0.096 0.094 1 +8.700 0.102 0.088 1 +8.900 0.112 0.087 1 +9.100 0.126 0.087 1 +9.300 0.147 0.087 1 +9.500 0.176 0.084 1 +9.700 0.214 0.079 1 +9.900 0.261 0.076 1 +10.100 0.314 0.077 1 +10.300 0.369 0.073 1 +10.500 0.418 0.066 1 +10.700 0.462 0.067 1 +10.900 0.511 0.076 1 +11.100 0.590 0.090 1 +11.300 0.726 0.170 1 +11.500 0.891 0.418 1 +11.700 0.983 0.911 1 +11.900 0.999 1.814 1 +# 16 0.5 0.8 0.0 0.7 Ilbert 2013 +8.900 0.019 0.113 1 +9.100 0.030 0.093 1 +9.300 0.046 0.088 1 +9.500 0.070 0.085 1 +9.700 0.105 0.079 1 +9.900 0.155 0.075 1 +10.100 0.221 0.070 1 +10.300 0.300 0.062 1 +10.500 0.386 0.052 1 +10.700 0.474 0.044 1 +10.900 0.569 0.047 1 +11.100 0.685 0.055 1 +11.300 0.823 0.108 1 +11.500 0.942 0.242 1 +11.700 0.992 0.455 1 +11.900 1.000 0.796 1 +# 14 0.8 1.1 0.0 0.7 Ilbert 2013 +9.300 0.024 0.100 1 +9.500 0.043 0.080 1 +9.700 0.077 0.074 1 +9.900 0.131 0.070 1 +10.100 0.212 0.062 1 +10.300 0.318 0.045 1 +10.500 0.439 0.035 1 +10.700 0.561 0.030 1 +10.900 0.667 0.036 1 +11.100 0.752 0.056 1 +11.300 0.817 0.102 1 +11.500 0.866 0.200 1 +11.700 0.902 0.369 1 +11.900 0.930 0.675 1 +# 13 1.1 1.5 0.0 0.7 Ilbert 2013 +9.500 0.015 0.088 1 +9.700 0.029 0.073 1 +9.900 0.053 0.071 1 +10.100 0.094 0.069 1 +10.300 0.159 0.066 1 +10.500 0.246 0.060 1 +10.700 0.345 0.052 1 +10.900 0.440 0.046 1 +11.100 0.523 0.051 1 +11.300 0.600 0.075 1 +11.500 0.684 0.152 1 +11.700 0.784 0.325 1 +11.900 0.888 0.721 1 +# 12 1.5 2.0 0.0 0.7 Ilbert 2013 +9.700 0.010 0.096 1 +9.900 0.021 0.071 1 +10.100 0.042 0.066 1 +10.300 0.074 0.072 1 +10.500 0.118 0.068 1 +10.700 0.169 0.057 1 +10.900 0.223 0.070 1 +11.100 0.285 0.073 1 +11.300 0.366 0.099 1 +11.500 0.482 0.167 1 +11.700 0.649 0.307 1 +11.900 0.838 0.822 1 +# 10 2.0 2.5 0.0 0.7 Ilbert 2013 +9.900 0.007 0.141 1 +10.100 0.019 0.091 1 +10.300 0.043 0.088 1 +10.500 0.085 0.084 1 +10.700 0.146 0.067 1 +10.900 0.215 0.076 1 +11.100 0.271 0.085 1 +11.300 0.288 0.133 1 +11.500 0.243 0.320 1 +11.700 0.135 0.856 1 +# 9 2.5 3.0 0.0 0.7 Ilbert 2013 +10.100 0.001 0.177 1 +10.300 0.006 0.131 1 +10.500 0.029 0.129 1 +10.700 0.096 0.127 1 +10.900 0.189 0.105 1 +11.100 0.193 0.151 1 +11.300 0.071 0.276 1 +11.500 0.004 0.617 1 +11.700 0.000 1.397 1 +# 19 0.2 0.3 0.0 0.7 Moustakas 2013 +9.300 0.065 0.122 1 +9.400 0.156 0.061 1 +9.500 0.164 0.058 1 +9.600 0.216 0.049 1 +9.700 0.186 0.061 1 +9.800 0.248 0.050 1 +9.900 0.215 0.054 1 +10.000 0.289 0.053 1 +10.100 0.353 0.047 1 +10.200 0.426 0.041 1 +10.300 0.432 0.044 1 +10.400 0.569 0.045 1 +10.500 0.417 0.046 1 +10.600 0.494 0.046 1 +10.700 0.550 0.049 1 +10.800 0.554 0.060 1 +10.900 0.711 0.084 1 +11.000 0.746 0.122 1 +11.100 0.715 0.123 1 +# 20 0.3 0.4 0.0 0.7 Moustakas 2013 +9.600 0.173 0.052 1 +9.700 0.172 0.049 1 +9.800 0.226 0.045 1 +9.900 0.229 0.045 1 +10.000 0.285 0.044 1 +10.100 0.314 0.046 1 +10.200 0.347 0.033 1 +10.300 0.364 0.033 1 +10.400 0.405 0.032 1 +10.500 0.449 0.031 1 +10.600 0.507 0.032 1 +10.700 0.531 0.035 1 +10.800 0.595 0.041 1 +10.900 0.528 0.049 1 +11.000 0.605 0.055 1 +11.100 0.755 0.079 1 +11.200 0.725 0.109 1 +11.300 0.799 0.149 1 +11.400 0.729 0.221 1 +11.500 0.420 1.030 1 +# 18 0.4 0.5 0.0 0.7 Moustakas 2013 +9.900 0.194 0.050 1 +10.000 0.216 0.043 1 +10.100 0.221 0.137 1 +10.200 0.308 0.040 1 +10.300 0.304 0.036 1 +10.400 0.395 0.038 1 +10.500 0.403 0.035 1 +10.600 0.416 0.033 1 +10.700 0.444 0.037 1 +10.800 0.461 0.034 1 +10.900 0.536 0.044 1 +11.000 0.585 0.046 1 +11.100 0.573 0.061 1 +11.200 0.626 0.080 1 +11.300 0.569 0.105 1 +11.400 0.725 0.219 1 +11.500 0.751 0.486 1 +11.600 0.780 0.618 1 +# 16 0.5 0.65 0.0 0.7 Moustakas 2013 +10.200 0.242 0.033 1 +10.300 0.375 0.029 1 +10.400 0.386 0.025 1 +10.500 0.398 0.025 1 +10.600 0.422 0.024 1 +10.700 0.444 0.023 1 +10.800 0.513 0.024 1 +10.900 0.582 0.030 1 +11.000 0.611 0.032 1 +11.100 0.642 0.041 1 +11.200 0.621 0.053 1 +11.300 0.759 0.089 1 +11.400 0.629 0.103 1 +11.500 0.863 0.174 1 +11.600 0.849 0.504 1 +11.700 0.751 0.389 1 +# 13 0.65 0.8 0.0 0.7 Moustakas 2013 +10.500 0.444 0.029 1 +10.600 0.406 0.042 1 +10.700 0.404 0.037 1 +10.800 0.499 0.022 1 +10.900 0.500 0.026 1 +11.000 0.603 0.042 1 +11.100 0.580 0.033 1 +11.200 0.571 0.051 1 +11.300 0.635 0.057 1 +11.400 0.731 0.081 1 +11.500 0.686 0.192 1 +11.600 0.855 0.350 1 +11.700 0.299 0.672 1 +# 11 0.8 1.0 0.0 0.7 Moustakas 2013 +10.800 0.527 0.032 1 +10.900 0.556 0.027 1 +11.000 0.613 0.034 1 +11.100 0.634 0.036 1 +11.200 0.570 0.039 1 +11.300 0.537 0.044 1 +11.400 0.661 0.101 1 +11.500 0.651 0.170 1 +11.600 0.814 0.157 1 +11.700 0.574 0.352 1 +11.800 0.552 1.101 1 +# 32 0.2 0.5 0.03 0.7 Muzzin 2013 +8.4 0.142 0.157 1 +8.5 0.122 0.100 1 +8.6 0.163 0.084 1 +8.7 0.153 0.084 1 +8.8 0.133 0.088 1 +8.9 0.137 0.086 1 +9.0 0.156 0.087 1 +9.1 0.127 0.090 1 +9.2 0.129 0.090 1 +9.3 0.126 0.095 1 +9.4 0.128 0.095 1 +9.5 0.159 0.088 1 +9.6 0.179 0.086 1 +9.7 0.204 0.079 1 +9.8 0.198 0.081 1 +9.9 0.261 0.072 1 +10.0 0.226 0.079 1 +10.1 0.296 0.070 1 +10.2 0.332 0.068 1 +10.3 0.359 0.064 1 +10.4 0.408 0.058 1 +10.5 0.460 0.055 1 +10.6 0.501 0.055 1 +10.7 0.615 0.068 1 +10.8 0.629 0.074 1 +10.9 0.730 0.091 1 +11.0 0.763 0.104 1 +11.1 0.842 0.143 1 +11.2 0.900 0.181 1 +11.3 0.872 0.169 1 +11.4 0.943 0.237 1 +11.5 0.920 0.285 1 +# 27 0.5 1.0 0.03 0.7 Muzzin 2013 +8.9 0.065 0.080 1 +9.0 0.056 0.063 1 +9.1 0.068 0.063 1 +9.2 0.066 0.064 1 +9.3 0.069 0.057 1 +9.4 0.076 0.057 1 +9.5 0.067 0.059 1 +9.6 0.088 0.058 1 +9.7 0.103 0.055 1 +9.8 0.115 0.053 1 +9.9 0.174 0.048 1 +10.0 0.202 0.047 1 +10.1 0.241 0.045 1 +10.2 0.295 0.039 1 +10.3 0.307 0.038 1 +10.4 0.361 0.033 1 +10.5 0.412 0.032 1 +10.6 0.451 0.032 1 +10.7 0.484 0.034 1 +10.8 0.556 0.036 1 +10.9 0.601 0.040 1 +11.0 0.634 0.051 1 +11.1 0.751 0.067 1 +11.2 0.777 0.083 1 +11.3 0.854 0.110 1 +11.4 0.892 0.169 1 +11.5 0.960 0.206 1 +# 23 1.0 1.5 0.03 0.7 Muzzin 2013 +9.5 0.046 0.126 1 +9.6 0.065 0.065 1 +9.7 0.076 0.056 1 +9.8 0.104 0.056 1 +9.9 0.117 0.048 1 +10.0 0.157 0.045 1 +10.1 0.184 0.045 1 +10.2 0.220 0.043 1 +10.3 0.287 0.036 1 +10.4 0.311 0.036 1 +10.5 0.360 0.033 1 +10.6 0.413 0.034 1 +10.7 0.449 0.034 1 +10.8 0.509 0.033 1 +10.9 0.526 0.037 1 +11.0 0.620 0.043 1 +11.1 0.668 0.057 1 +11.2 0.708 0.071 1 +11.3 0.761 0.116 1 +11.4 0.855 0.146 1 +11.5 0.786 0.229 1 +11.6 0.941 0.376 1 +11.7 0.800 0.549 1 +# 16 1.5 2.0 0.03 0.7 Muzzin 2013 +10.1 0.133 0.076 1 +10.2 0.191 0.068 1 +10.3 0.201 0.060 1 +10.4 0.249 0.047 1 +10.5 0.301 0.044 1 +10.6 0.319 0.042 1 +10.7 0.374 0.041 1 +10.8 0.389 0.047 1 +10.9 0.442 0.043 1 +11.0 0.398 0.056 1 +11.1 0.477 0.061 1 +11.2 0.537 0.067 1 +11.3 0.487 0.092 1 +11.4 0.617 0.144 1 +11.5 0.647 0.212 1 +11.6 0.667 0.606 1 +# 13 2.0 2.5 0.03 0.7 Muzzin 2013 +10.5 0.246 0.079 1 +10.6 0.239 0.080 1 +10.7 0.249 0.070 1 +10.8 0.245 0.089 1 +10.9 0.181 0.108 1 +11.0 0.278 0.083 1 +11.1 0.238 0.107 1 +11.2 0.150 0.156 1 +11.3 0.274 0.147 1 +11.4 0.400 0.184 1 +11.5 0.600 0.468 1 +11.6 0.143 0.521 1 +11.7 0.250 0.622 1 +# 6 2.5 3.0 0.03 0.7 Muzzin 2013 +10.8 0.233 0.130 1 +11.0 0.234 0.104 1 +11.2 0.170 0.142 1 +11.4 0.177 0.180 1 +11.6 0.059 0.421 1 +11.8 0.167 0.591 1 +# 6 3.0 4.0 0.03 0.7 Muzzin 2013 +11.0 0.134 0.225 1 +11.2 0.149 0.260 1 +11.4 0.235 0.253 1 +11.6 0.095 0.377 1 +11.8 0.000 0.730 1 +12.0 0.000 0.999 1 +END diff --git a/data/smf.dat b/data/smf.dat new file mode 100644 index 0000000..36e7a48 --- /dev/null +++ b/data/smf.dat @@ -0,0 +1,1146 @@ +# Header: Npoints zmin zmax IMF Hubble Comment +# 39 0.0 0.2 0.0 1.0 Li & White 2009 +8.05 -1.216 -1.174 -1.263 1 +8.15 -1.312 -1.264 -1.366 1 +8.25 -1.316 -1.275 -1.362 1 +8.35 -1.327 -1.288 -1.369 1 +8.45 -1.344 -1.308 -1.383 1 +8.55 -1.359 -1.325 -1.396 1 +8.65 -1.374 -1.345 -1.405 1 +8.75 -1.418 -1.386 -1.451 1 +8.85 -1.439 -1.411 -1.469 1 +8.95 -1.478 -1.450 -1.507 1 +9.05 -1.514 -1.488 -1.543 1 +9.15 -1.574 -1.547 -1.602 1 +9.25 -1.599 -1.573 -1.626 1 +9.35 -1.634 -1.606 -1.664 1 +9.45 -1.664 -1.638 -1.691 1 +9.55 -1.671 -1.648 -1.695 1 +9.65 -1.693 -1.670 -1.717 1 +9.75 -1.707 -1.687 -1.728 1 +9.85 -1.706 -1.686 -1.726 1 +9.95 -1.721 -1.704 -1.740 1 +10.05 -1.744 -1.729 -1.759 1 +10.15 -1.796 -1.782 -1.811 1 +10.25 -1.854 -1.840 -1.868 1 +10.35 -1.922 -1.908 -1.935 1 +10.45 -2.006 -1.994 -2.019 1 +10.55 -2.110 -2.098 -2.123 1 +10.65 -2.249 -2.237 -2.262 1 +10.75 -2.420 -2.407 -2.434 1 +10.85 -2.617 -2.607 -2.627 1 +10.95 -2.863 -2.851 -2.875 1 +11.05 -3.150 -3.134 -3.167 1 +11.15 -3.475 -3.462 -3.488 1 +11.25 -3.856 -3.839 -3.875 1 +11.35 -4.278 -4.253 -4.306 1 +11.45 -4.745 -4.702 -4.794 1 +11.55 -5.312 -5.237 -5.403 1 +11.65 -5.866 -5.745 -6.032 1 +11.75 -6.436 -6.297 -6.642 1 +11.85 -6.782 -6.589 -7.137 1 +# 21 0.0 0.06 0.0 0.7 Baldry 2012 +7.30 -1.366 -1.286 -1.463 1 +7.50 -1.500 -1.391 -1.646 1 +7.70 -1.458 -1.365 -1.578 1 +7.90 -1.564 -1.502 -1.636 1 +8.10 -1.548 -1.507 -1.593 1 +8.30 -1.629 -1.577 -1.688 1 +8.50 -1.717 -1.690 -1.745 1 +8.70 -1.745 -1.686 -1.812 1 +8.90 -1.845 -1.796 -1.900 1 +9.10 -1.991 -1.967 -2.018 1 +9.30 -2.018 -1.994 -2.044 1 +9.50 -2.130 -2.106 -2.154 1 +9.70 -2.207 -2.182 -2.234 1 +9.90 -2.243 -2.218 -2.271 1 +10.10 -2.259 -2.233 -2.287 1 +10.30 -2.261 -2.235 -2.289 1 +10.50 -2.291 -2.264 -2.320 1 +10.70 -2.450 -2.418 -2.484 1 +10.90 -2.618 -2.578 -2.662 1 +11.10 -2.896 -2.845 -2.955 1 +11.30 -3.471 -3.374 -3.597 1 +# 32 0.0 0.2 0.0 0.7 Bernardi 2013 +9.05 -2.022 -1.972 -2.072 1 +9.15 -2.005 -1.962 -2.048 1 +9.25 -1.989 -1.952 -2.026 1 +9.35 -2.028 -1.995 -2.061 1 +9.45 -2.060 -2.030 -2.090 1 +9.55 -2.081 -2.054 -2.108 1 +9.65 -2.110 -2.085 -2.135 1 +9.75 -2.155 -2.133 -2.177 1 +9.85 -2.162 -2.142 -2.182 1 +9.95 -2.175 -2.157 -2.193 1 +10.05 -2.209 -2.192 -2.226 1 +10.15 -2.211 -2.196 -2.226 1 +10.25 -2.223 -2.210 -2.236 1 +10.35 -2.240 -2.228 -2.252 1 +10.45 -2.241 -2.230 -2.252 1 +10.55 -2.285 -2.275 -2.295 1 +10.65 -2.339 -2.329 -2.349 1 +10.75 -2.401 -2.392 -2.410 1 +10.85 -2.486 -2.477 -2.495 1 +10.95 -2.591 -2.582 -2.600 1 +11.05 -2.721 -2.712 -2.730 1 +11.15 -2.889 -2.879 -2.899 1 +11.25 -3.097 -3.086 -3.108 1 +11.35 -3.345 -3.332 -3.358 1 +11.45 -3.625 -3.609 -3.641 1 +11.55 -3.934 -3.914 -3.954 1 +11.65 -4.279 -4.252 -4.306 1 +11.75 -4.676 -4.638 -4.714 1 +11.85 -5.133 -5.074 -5.192 1 +11.95 -5.757 -5.647 -5.867 1 +12.05 -6.208 -6.035 -6.381 1 +12.15 -6.949 -6.556 -7.342 1 +# 20 0.00 0.20 0.24 0.7 Perez-Gonzalez 2008 +8.00 -1.275 -1.128 -1.440 1 +8.20 -1.325 -1.175 -1.494 1 +8.40 -1.328 -1.173 -1.503 1 +8.60 -1.485 -1.302 -1.698 1 +8.80 -1.596 -1.407 -1.817 1 +9.00 -1.698 -1.493 -1.942 1 +9.20 -1.767 -1.548 -2.030 1 +9.40 -1.806 -1.574 -2.089 1 +9.60 -1.823 -1.634 -2.044 1 +9.80 -1.890 -1.665 -2.163 1 +10.00 -1.965 -1.737 -2.242 1 +10.20 -1.931 -1.735 -2.162 1 +10.40 -2.052 -1.851 -2.290 1 +10.60 -2.122 -1.963 -2.303 1 +10.80 -2.177 -2.058 -2.308 1 +11.00 -2.311 -2.124 -2.529 1 +11.20 -2.591 -2.191 -12.590 1 +11.40 -3.022 -2.622 -13.021 1 +11.60 -3.327 -2.927 -13.326 1 +11.80 -3.328 -2.418 -13.327 1 +# 20 0.20 0.40 0.24 0.7 Perez-Gonzalez 2008 +8.20 -1.690 -1.567 -1.826 1 +8.40 -1.681 -1.546 -1.831 1 +8.60 -1.817 -1.681 -1.968 1 +8.80 -1.800 -1.665 -1.951 1 +9.00 -1.897 -1.760 -2.050 1 +9.20 -1.987 -1.814 -2.187 1 +9.40 -1.953 -1.711 -2.165 1 +9.60 -2.042 -1.837 -2.285 1 +9.80 -2.145 -1.947 -2.379 1 +10.00 -2.113 -1.920 -2.339 1 +10.20 -2.207 -1.960 -2.513 1 +10.40 -2.263 -2.018 -2.565 1 +10.60 -2.329 -2.062 -2.665 1 +10.80 -2.499 -2.259 -2.793 1 +11.00 -2.605 -2.329 -2.955 1 +11.20 -2.798 -2.510 -3.168 1 +11.40 -2.955 -2.774 -3.166 1 +11.60 -3.458 -3.058 -13.457 1 +11.80 -4.040 -3.640 -9.990 1 +12.00 -5.037 -4.637 -9.990 1 +# 17 0.40 0.60 0.24 0.7 Perez-Gonzalez 2008 +8.80 -1.984 -1.875 -2.102 1 +9.00 -1.858 -1.763 -1.960 1 +9.20 -1.981 -1.868 -2.104 1 +9.40 -2.019 -1.905 -2.144 1 +9.60 -2.070 -1.942 -2.212 1 +9.80 -2.099 -1.981 -2.229 1 +10.00 -2.145 -2.007 -2.300 1 +10.20 -2.193 -2.057 -2.345 1 +10.40 -2.238 -2.081 -2.417 1 +10.60 -2.305 -2.150 -2.481 1 +10.80 -2.414 -2.227 -2.633 1 +11.00 -2.596 -2.388 -2.844 1 +11.20 -2.886 -2.615 -3.228 1 +11.40 -3.339 -3.023 -3.757 1 +11.60 -3.382 -3.207 -3.584 1 +11.80 -3.883 -3.635 -4.190 1 +12.00 -4.885 -4.485 -9.990 1 +# 14 0.60 0.80 0.24 0.7 Perez-Gonzalez 2008 +9.20 -2.067 -1.823 -2.367 1 +9.40 -2.041 -1.888 -2.214 1 +9.60 -2.050 -1.930 -2.182 1 +9.80 -2.064 -1.966 -2.170 1 +10.00 -2.124 -2.031 -2.224 1 +10.20 -2.174 -2.069 -2.288 1 +10.40 -2.254 -2.147 -2.371 1 +10.60 -2.367 -2.251 -2.495 1 +10.80 -2.509 -2.375 -2.659 1 +11.00 -2.650 -2.500 -2.820 1 +11.20 -2.830 -2.632 -3.063 1 +11.40 -3.280 -3.006 -3.628 1 +11.60 -3.607 -3.311 -3.990 1 +11.80 -4.042 -3.817 -4.315 1 +# 13 0.80 1.00 0.24 0.7 Perez-Gonzalez 2008 +9.40 -2.053 -1.975 -2.136 1 +9.60 -2.162 -2.079 -2.250 1 +9.80 -2.203 -2.114 -2.298 1 +10.00 -2.254 -2.164 -2.350 1 +10.20 -2.326 -2.223 -2.438 1 +10.40 -2.379 -2.275 -2.492 1 +10.60 -2.509 -2.391 -2.639 1 +10.80 -2.599 -2.481 -2.729 1 +11.00 -2.742 -2.583 -2.923 1 +11.20 -2.893 -2.677 -3.152 1 +11.40 -3.160 -2.890 -3.501 1 +11.60 -3.634 -3.377 -3.955 1 +11.80 -4.113 -3.918 -4.343 1 +# 13 1.00 1.30 0.24 0.7 Perez-Gonzalez 2008 +9.60 -2.285 -2.209 -2.366 1 +9.80 -2.294 -2.214 -2.380 1 +10.00 -2.355 -2.266 -2.450 1 +10.20 -2.468 -2.372 -2.571 1 +10.40 -2.508 -2.410 -2.613 1 +10.60 -2.563 -2.459 -2.676 1 +10.80 -2.647 -2.526 -2.780 1 +11.00 -2.788 -2.633 -2.963 1 +11.20 -3.032 -2.859 -3.231 1 +11.40 -3.392 -3.127 -3.725 1 +11.60 -3.678 -3.366 -4.088 1 +11.80 -4.232 -3.926 -4.632 1 +12.00 -4.663 -4.263 -9.990 1 +# 11 1.30 1.60 0.24 0.7 Perez-Gonzalez 2008 +10.00 -2.628 -2.525 -2.740 1 +10.20 -2.677 -2.571 -2.792 1 +10.40 -2.736 -2.619 -2.864 1 +10.60 -2.783 -2.656 -2.924 1 +10.80 -2.860 -2.724 -3.012 1 +11.00 -2.994 -2.851 -3.155 1 +11.20 -3.145 -2.965 -3.353 1 +11.40 -3.329 -3.073 -3.648 1 +11.60 -3.700 -3.419 -4.059 1 +11.80 -4.582 -4.182 -9.990 1 +12.00 -4.982 -4.779 -5.223 1 +# 10 1.60 2.00 0.24 0.7 Perez-Gonzalez 2008 +10.20 -2.935 -2.818 -3.063 1 +10.40 -2.979 -2.846 -3.127 1 +10.60 -2.967 -2.846 -3.101 1 +10.80 -3.066 -2.930 -3.218 1 +11.00 -3.207 -3.048 -3.389 1 +11.20 -3.340 -3.125 -3.598 1 +11.40 -3.585 -3.308 -3.938 1 +11.60 -3.873 -3.532 -4.335 1 +11.80 -4.388 -4.133 -4.706 1 +12.00 -4.834 -4.497 -5.290 1 +# 9 2.00 2.50 0.24 0.7 Perez-Gonzalez 2008 +10.40 -3.068 -2.949 -3.200 1 +10.60 -3.176 -3.037 -3.332 1 +10.80 -3.234 -3.095 -3.389 1 +11.00 -3.367 -3.202 -3.555 1 +11.20 -3.499 -3.323 -3.703 1 +11.40 -3.672 -3.446 -3.945 1 +11.60 -4.151 -3.883 -4.490 1 +11.80 -4.289 -4.035 -4.605 1 +12.00 -4.878 -4.542 -5.332 1 +# 7 2.50 3.00 0.24 0.7 Perez-Gonzalez 2008 +10.80 -3.477 -3.279 -3.710 1 +11.00 -3.495 -3.295 -3.731 1 +11.20 -3.591 -3.394 -3.823 1 +11.40 -3.770 -3.523 -4.076 1 +11.60 -4.156 -3.843 -4.569 1 +11.80 -4.385 -4.041 -4.853 1 +12.00 -4.986 -4.707 -5.342 1 +# 6 3.00 3.50 0.24 0.7 Perez-Gonzalez 2008 +11.00 -3.610 -3.340 -3.951 1 +11.20 -3.721 -3.484 -4.011 1 +11.40 -3.925 -3.663 -4.253 1 +11.60 -4.277 -4.006 -4.619 1 +11.80 -4.498 -4.167 -4.943 1 +12.00 -5.142 -4.742 -9.990 1 +# 6 3.50 4.00 0.24 0.7 Perez-Gonzalez 2008 +11.00 -3.748 -3.488 -4.073 1 +11.20 -3.816 -3.584 -4.098 1 +11.40 -4.084 -3.786 -4.471 1 +11.60 -4.355 -4.092 -4.685 1 +11.80 -4.923 -4.658 -5.256 1 +12.00 -5.101 -4.786 -5.517 1 +# 19 0.6 1.0 0.24 0.7 Santini 2012 +8.23 -1.301 -1.147 -1.456 1 +8.43 -1.449 -1.376 -1.522 1 +8.63 -1.731 -1.667 -1.794 1 +8.83 -1.804 -1.741 -1.867 1 +9.03 -1.798 -1.735 -1.862 1 +9.23 -1.932 -1.857 -2.007 1 +9.43 -2.089 -2.000 -2.179 1 +9.63 -2.148 -2.056 -2.241 1 +9.83 -2.248 -2.146 -2.352 1 +10.03 -2.318 -2.204 -2.434 1 +10.23 -2.318 -2.197 -2.440 1 +10.43 -2.504 -2.360 -2.650 1 +10.63 -2.534 -2.391 -2.680 1 +10.83 -2.534 -2.386 -2.685 1 +11.03 -2.566 -2.421 -2.715 1 +11.23 -3.203 -2.834 -3.607 1 +11.43 -3.078 -2.800 -3.381 1 +11.63 -3.379 -2.884 -3.936 1 +11.83 -3.680 -1.843 -5.600 1 +# 15 1.0 1.4 0.24 0.7 Santini 2012 +8.86 -1.447 -1.345 -1.551 1 +9.06 -1.898 -1.820 -1.977 1 +9.26 -2.055 -1.982 -2.128 1 +9.46 -2.177 -2.097 -2.257 1 +9.66 -2.296 -2.206 -2.387 1 +9.86 -2.284 -2.192 -2.377 1 +10.06 -2.442 -2.332 -2.554 1 +10.26 -2.562 -2.431 -2.694 1 +10.46 -2.518 -2.397 -2.641 1 +10.66 -2.664 -2.524 -2.808 1 +10.86 -2.886 -2.699 -3.080 1 +11.06 -2.726 -2.579 -2.878 1 +11.26 -2.937 -2.751 -3.132 1 +11.46 -3.363 -3.041 -3.725 1 +11.66 -3.840 -2.006 -5.758 1 +# 13 1.4 1.8 0.24 0.7 Santini 2012 +9.22 -1.569 -1.465 -1.674 1 +9.42 -2.085 -2.007 -2.164 1 +9.62 -2.326 -2.241 -2.412 1 +9.82 -2.497 -2.391 -2.604 1 +10.02 -2.570 -2.453 -2.688 1 +10.22 -2.708 -2.567 -2.851 1 +10.42 -2.611 -2.482 -2.742 1 +10.62 -2.633 -2.503 -2.765 1 +10.82 -3.067 -2.854 -3.290 1 +11.02 -2.871 -2.708 -3.039 1 +11.22 -3.009 -2.821 -3.206 1 +11.42 -3.611 -3.214 -4.084 1 +11.62 -3.912 -2.616 -5.324 1 +# 11 1.8 2.5 0.24 0.7 Santini 2012 +9.51 -1.605 -1.523 -1.688 1 +9.71 -1.820 -1.766 -1.874 1 +9.91 -2.290 -2.227 -2.353 1 +10.11 -2.456 -2.381 -2.533 1 +10.31 -2.712 -2.612 -2.813 1 +10.51 -2.698 -2.594 -2.802 1 +10.71 -2.888 -2.762 -3.015 1 +10.91 -2.910 -2.779 -3.043 1 +11.11 -2.985 -2.840 -3.132 1 +11.31 -3.286 -3.088 -3.492 1 +11.51 -3.888 -3.463 -4.385 1 +# 9 2.5 3.5 0.24 0.7 Santini 2012 +9.88 -2.564 -2.351 -2.644 1 +10.08 -2.560 -2.402 -2.617 1 +10.28 -2.793 -2.619 -2.851 1 +10.48 -3.136 -2.922 -3.205 1 +10.68 -3.395 -3.136 -3.501 1 +10.88 -3.437 -3.167 -3.553 1 +11.08 -3.659 -3.329 -3.840 1 +11.28 -4.136 -3.630 -4.541 1 +11.48 -4.437 -3.381 -5.523 1 +# 8 3.5 4.5 0.24 0.7 Santini 2012 +10.22 -2.796 -2.550 -2.914 1 +10.42 -3.175 -2.947 -3.250 1 +10.62 -3.498 -3.209 -3.631 1 +10.82 -3.924 -3.482 -4.240 1 +11.02 -4.100 -3.590 -4.511 1 +11.22 -3.924 -3.479 -4.244 1 +11.42 -4.100 -3.508 -4.609 1 +11.82 -4.401 -2.747 -6.074 1 +# 15 0.2 0.5 0.0 0.7 Ilbert 2013 +8.075 -1.602 -1.551 -1.660 1 +8.360 -1.623 -1.573 -1.679 1 +8.645 -1.738 -1.688 -1.794 1 +8.930 -1.842 -1.793 -1.898 1 +9.215 -1.969 -1.919 -2.025 1 +9.500 -2.096 -2.046 -2.152 1 +9.785 -2.208 -2.157 -2.265 1 +10.070 -2.284 -2.233 -2.342 1 +10.355 -2.330 -2.278 -2.390 1 +10.640 -2.399 -2.345 -2.462 1 +10.925 -2.579 -2.520 -2.647 1 +11.210 -2.955 -2.888 -3.034 1 +11.495 -3.477 -3.393 -3.581 1 +11.780 -4.227 -4.093 -4.423 1 +12.065 -5.217 -4.899 -9.117 1 +# 13 0.5 0.8 0.0 0.7 Ilbert 2013 +8.800 -1.944 -1.906 -1.986 1 +9.050 -1.997 -1.960 -2.039 1 +9.300 -2.110 -2.072 -2.152 1 +9.550 -2.247 -2.208 -2.289 1 +9.800 -2.385 -2.346 -2.427 1 +10.050 -2.448 -2.408 -2.491 1 +10.300 -2.490 -2.450 -2.536 1 +10.550 -2.579 -2.536 -2.627 1 +10.800 -2.731 -2.684 -2.783 1 +11.050 -2.998 -2.945 -3.059 1 +11.300 -3.467 -3.401 -3.544 1 +11.550 -3.969 -3.880 -4.081 1 +11.800 -5.168 -4.977 -5.518 1 +# 12 0.8 1.1 0.0 0.7 Ilbert 2013 +9.250 -2.142 -2.108 -2.178 1 +9.500 -2.227 -2.193 -2.264 1 +9.750 -2.348 -2.313 -2.385 1 +10.000 -2.459 -2.424 -2.498 1 +10.250 -2.517 -2.480 -2.556 1 +10.500 -2.542 -2.504 -2.584 1 +10.750 -2.616 -2.575 -2.662 1 +11.000 -2.805 -2.758 -2.857 1 +11.250 -3.138 -3.084 -3.200 1 +11.500 -3.751 -3.681 -3.834 1 +11.750 -4.692 -4.570 -4.863 1 +12.000 -5.354 -5.126 -5.863 1 +# 10 1.1 1.5 0.0 0.7 Ilbert 2013 +9.500 -2.270 -2.234 -2.291 1 +9.750 -2.353 -2.311 -2.369 1 +10.000 -2.476 -2.437 -2.503 1 +10.250 -2.583 -2.553 -2.610 1 +10.500 -2.660 -2.624 -2.678 1 +10.750 -2.767 -2.728 -2.804 1 +11.000 -3.011 -2.968 -3.047 1 +11.250 -3.478 -3.417 -3.526 1 +11.500 -4.335 -4.247 -4.425 1 +11.750 -5.152 -4.998 -5.400 1 +# 10 1.5 2.0 0.0 0.7 Ilbert 2013 +9.500 -2.287 -2.261 -2.315 1 +9.750 -2.371 -2.344 -2.399 1 +10.000 -2.497 -2.470 -2.526 1 +10.250 -2.609 -2.581 -2.640 1 +10.500 -2.682 -2.651 -2.716 1 +10.750 -2.794 -2.758 -2.832 1 +11.000 -3.036 -2.994 -3.082 1 +11.250 -3.492 -3.439 -3.552 1 +11.500 -4.350 -4.271 -4.447 1 +11.750 -5.166 -5.010 -5.411 1 +# 12 2.0 2.5 0.0 0.7 Ilbert 2013 +9.550 -2.809 -2.817 -2.800 1 +9.750 -2.826 -2.834 -2.818 1 +9.950 -3.007 -2.978 -3.039 1 +10.150 -3.052 -3.021 -3.086 1 +10.350 -3.079 -3.045 -3.115 1 +10.550 -3.136 -3.099 -3.177 1 +10.750 -3.223 -3.181 -3.269 1 +10.950 -3.372 -3.324 -3.427 1 +11.150 -3.643 -3.586 -3.710 1 +11.350 -4.037 -3.964 -4.124 1 +11.550 -4.468 -4.370 -4.594 1 +11.750 -5.247 -5.088 -5.500 1 +# 11 2.5 3.0 0.0 0.7 Ilbert 2013 +10.160 -2.999 -3.007 -2.990 1 +10.320 -3.221 -3.179 -3.268 1 +10.480 -3.288 -3.243 -3.338 1 +10.640 -3.407 -3.358 -3.462 1 +10.800 -3.469 -3.415 -3.531 1 +10.960 -3.615 -3.554 -3.686 1 +11.120 -3.810 -3.740 -3.895 1 +11.280 -4.092 -4.008 -4.197 1 +11.440 -4.571 -4.459 -4.721 1 +11.600 -4.898 -4.744 -5.138 1 +11.760 -5.638 -5.392 -6.263 1 +# 11 3.0 4.0 0.0 0.7 Ilbert 2013 +10.180 -3.381 -3.396 -3.366 1 +10.340 -3.578 -3.535 -3.625 1 +10.500 -3.711 -3.665 -3.762 1 +10.660 -3.928 -3.874 -3.990 1 +10.820 -4.008 -3.945 -4.081 1 +10.980 -4.213 -4.142 -4.297 1 +11.140 -4.302 -4.217 -4.406 1 +11.300 -4.661 -4.551 -4.808 1 +11.460 -4.980 -4.866 -5.135 1 +11.620 -5.019 -4.838 -5.338 1 +11.780 -5.854 -5.530 -9.754 1 +# 10 0.05 0.2 0.0 0.7 Ilbert 2010 +7.260 -0.956 -0.883 -1.044 1 +7.690 -1.119 -1.091 -1.149 1 +8.120 -1.369 -1.347 -1.393 1 +8.550 -1.708 -1.691 -1.725 1 +8.980 -1.922 -1.903 -1.941 1 +9.410 -2.148 -2.125 -2.174 1 +9.840 -2.291 -2.263 -2.321 1 +10.270 -2.440 -2.407 -2.475 1 +10.700 -2.675 -2.633 -2.723 1 +11.130 -3.131 -3.060 -3.215 1 +# 10 0.2 0.4 0.0 0.7 Ilbert 2010 +8.950 -1.853 -1.843 -1.863 1 +9.250 -1.993 -1.983 -2.004 1 +9.550 -2.088 -2.076 -2.099 1 +9.850 -2.183 -2.171 -2.196 1 +10.150 -2.241 -2.228 -2.255 1 +10.450 -2.280 -2.266 -2.294 1 +10.750 -2.489 -2.471 -2.507 1 +11.050 -2.763 -2.739 -2.789 1 +11.350 -3.254 -3.213 -3.300 1 +11.650 -4.144 -4.038 -4.286 1 +# 10 0.4 0.6 0.0 0.7 Ilbert 2010 +9.290 -2.224 -2.214 -2.234 1 +9.575 -2.373 -2.362 -2.384 1 +9.860 -2.471 -2.459 -2.483 1 +10.145 -2.504 -2.491 -2.517 1 +10.430 -2.541 -2.528 -2.555 1 +10.715 -2.636 -2.622 -2.651 1 +11.000 -2.854 -2.836 -2.874 1 +11.285 -3.392 -3.358 -3.429 1 +11.570 -3.993 -3.927 -4.070 1 +11.855 -5.095 -4.897 -5.469 1 +# 10 0.6 0.8 0.0 0.7 Ilbert 2010 +9.480 -2.269 -2.260 -2.278 1 +9.720 -2.342 -2.333 -2.351 1 +9.960 -2.420 -2.410 -2.431 1 +10.200 -2.507 -2.496 -2.518 1 +10.440 -2.548 -2.536 -2.560 1 +10.680 -2.618 -2.606 -2.631 1 +10.920 -2.799 -2.783 -2.815 1 +11.160 -3.115 -3.093 -3.138 1 +11.400 -3.712 -3.669 -3.759 1 +11.640 -4.368 -4.282 -4.475 1 +# 10 0.8 1.0 0.0 0.7 Ilbert 2010 +9.680 -2.376 -2.367 -2.384 1 +9.900 -2.400 -2.391 -2.409 1 +10.120 -2.420 -2.410 -2.429 1 +10.340 -2.468 -2.458 -2.478 1 +10.560 -2.512 -2.502 -2.522 1 +10.780 -2.575 -2.564 -2.586 1 +11.000 -2.804 -2.790 -2.818 1 +11.220 -3.116 -3.096 -3.136 1 +11.440 -3.680 -3.642 -3.720 1 +11.660 -4.476 -4.388 -4.586 1 +# 10 1.0 1.2 0.0 0.7 Ilbert 2010 +9.890 -2.554 -2.544 -2.565 1 +10.070 -2.599 -2.587 -2.610 1 +10.250 -2.644 -2.633 -2.657 1 +10.430 -2.642 -2.630 -2.654 1 +10.610 -2.714 -2.701 -2.727 1 +10.790 -2.820 -2.806 -2.835 1 +10.970 -3.000 -2.983 -3.019 1 +11.150 -3.297 -3.273 -3.323 1 +11.330 -3.789 -3.747 -3.835 1 +11.510 -4.329 -4.253 -4.420 1 +# 8 1.2 1.5 0.0 0.7 Ilbert 2010 +10.570 -2.885 -2.872 -2.898 1 +10.720 -2.930 -2.917 -2.944 1 +10.870 -3.083 -3.067 -3.100 1 +11.020 -3.271 -3.252 -3.292 1 +11.170 -3.611 -3.582 -3.642 1 +11.320 -3.964 -3.922 -4.011 1 +11.470 -4.742 -4.645 -4.867 1 +11.620 -5.645 -5.413 -6.179 1 +# 10 1.5 2.0 0.0 0.7 Ilbert 2010 +11.210 -3.984 -3.934 -4.041 1 +11.270 -3.891 -3.845 -3.942 1 +11.330 -4.039 -3.985 -4.100 1 +11.390 -4.249 -4.182 -4.328 1 +11.450 -4.691 -4.585 -4.832 1 +11.510 -5.027 -4.878 -5.255 1 +11.570 -4.851 -4.726 -5.027 1 +11.630 -5.504 -5.272 -6.038 1 +11.690 -5.328 -5.130 -5.702 1 +11.750 -5.805 -5.504 -6.805 1 +# 26 0.2 0.3 0.0 0.7 Moustakas 2013 +8.800 -2.009 -1.955 -2.057 1 +8.900 -2.039 -1.983 -2.089 1 +9.000 -2.160 -2.115 -2.201 1 +9.100 -2.185 -2.135 -2.230 1 +9.200 -2.078 -2.012 -2.135 1 +9.300 -2.085 -2.024 -2.139 1 +9.400 -2.142 -2.104 -2.177 1 +9.500 -2.155 -2.119 -2.188 1 +9.600 -2.124 -2.088 -2.157 1 +9.700 -2.200 -2.163 -2.234 1 +9.800 -2.212 -2.178 -2.243 1 +9.900 -2.242 -2.208 -2.274 1 +10.000 -2.215 -2.177 -2.250 1 +10.100 -2.320 -2.283 -2.354 1 +10.200 -2.285 -2.250 -2.318 1 +10.300 -2.330 -2.292 -2.365 1 +10.400 -2.350 -2.312 -2.385 1 +10.500 -2.380 -2.341 -2.416 1 +10.600 -2.396 -2.355 -2.433 1 +10.700 -2.422 -2.379 -2.461 1 +10.800 -2.542 -2.490 -2.589 1 +10.900 -2.642 -2.579 -2.697 1 +11.000 -2.784 -2.695 -2.859 1 +11.100 -2.830 -2.730 -2.914 1 +11.200 -3.170 -2.770 -3.370 1 +11.300 -3.540 -3.240 -3.740 1 +# 24 0.3 0.4 0.0 0.7 Moustakas 2013 +9.200 -2.132 -2.089 -2.172 1 +9.300 -2.210 -2.168 -2.248 1 +9.400 -2.190 -2.111 -2.258 1 +9.500 -2.183 -2.128 -2.232 1 +9.600 -2.282 -2.250 -2.312 1 +9.700 -2.258 -2.228 -2.286 1 +9.800 -2.235 -2.197 -2.270 1 +9.900 -2.241 -2.212 -2.268 1 +10.000 -2.208 -2.177 -2.237 1 +10.100 -2.288 -2.255 -2.318 1 +10.200 -2.241 -2.215 -2.265 1 +10.300 -2.233 -2.206 -2.258 1 +10.400 -2.290 -2.263 -2.315 1 +10.500 -2.283 -2.256 -2.308 1 +10.600 -2.332 -2.304 -2.359 1 +10.700 -2.407 -2.376 -2.436 1 +10.800 -2.472 -2.438 -2.504 1 +10.900 -2.579 -2.537 -2.618 1 +11.000 -2.709 -2.663 -2.751 1 +11.100 -2.819 -2.767 -2.866 1 +11.200 -3.109 -3.033 -3.174 1 +11.300 -3.340 -3.240 -3.426 1 +11.400 -3.580 -3.380 -3.680 1 +11.500 -4.340 -3.940 -4.540 1 +# 23 0.4 0.5 0.0 0.7 Moustakas 2013 +9.600 -2.292 -2.220 -2.355 1 +9.700 -2.347 -2.316 -2.376 1 +9.800 -2.289 -2.259 -2.317 1 +9.900 -2.308 -2.268 -2.344 1 +10.000 -2.325 -2.297 -2.352 1 +10.100 -2.253 -2.166 -2.326 1 +10.200 -2.342 -2.312 -2.370 1 +10.300 -2.372 -2.345 -2.397 1 +10.400 -2.327 -2.291 -2.360 1 +10.500 -2.332 -2.302 -2.360 1 +10.600 -2.384 -2.356 -2.410 1 +10.700 -2.360 -2.327 -2.391 1 +10.800 -2.493 -2.464 -2.521 1 +10.900 -2.644 -2.605 -2.680 1 +11.000 -2.734 -2.695 -2.770 1 +11.100 -2.978 -2.926 -3.025 1 +11.200 -3.114 -3.048 -3.171 1 +11.300 -3.460 -3.360 -3.543 1 +11.400 -3.670 -3.570 -3.770 1 +11.500 -4.120 -3.820 -4.320 1 +11.600 -4.350 -3.950 -4.550 1 +11.700 -5.090 -4.090 -5.490 1 +11.800 -5.050 -4.050 -5.450 1 +# 18 0.5 0.65 0.0 0.7 Moustakas 2013 +10.000 -2.419 -2.383 -2.452 1 +10.100 -2.394 -2.367 -2.420 1 +10.200 -2.371 -2.349 -2.392 1 +10.300 -2.388 -2.361 -2.413 1 +10.400 -2.382 -2.361 -2.402 1 +10.500 -2.346 -2.326 -2.365 1 +10.600 -2.408 -2.388 -2.427 1 +10.700 -2.431 -2.411 -2.450 1 +10.800 -2.502 -2.481 -2.522 1 +10.900 -2.602 -2.577 -2.625 1 +11.000 -2.729 -2.702 -2.754 1 +11.100 -2.921 -2.888 -2.952 1 +11.200 -3.118 -3.076 -3.157 1 +11.300 -3.311 -3.252 -3.363 1 +11.400 -3.649 -3.566 -3.720 1 +11.500 -3.800 -3.700 -3.900 1 +11.600 -4.520 -4.320 -4.720 1 +11.700 -4.210 -4.010 -4.410 1 +# 17 0.65 0.8 0.0 0.7 Moustakas 2013 +10.400 -2.387 -2.365 -2.408 1 +10.500 -2.320 -2.293 -2.345 1 +10.600 -2.353 -2.320 -2.384 1 +10.700 -2.387 -2.358 -2.414 1 +10.800 -2.443 -2.423 -2.462 1 +10.900 -2.487 -2.464 -2.509 1 +11.000 -2.599 -2.566 -2.630 1 +11.100 -2.772 -2.744 -2.798 1 +11.200 -2.919 -2.877 -2.958 1 +11.300 -3.233 -3.188 -3.274 1 +11.400 -3.470 -3.414 -3.520 1 +11.500 -3.930 -3.830 -4.027 1 +11.600 -4.220 -4.020 -4.320 1 +11.700 -4.600 -4.300 -4.800 1 +11.800 -4.940 -3.940 -5.340 1 +11.900 -4.480 -3.480 -4.880 1 +12.000 -5.480 -4.480 -5.880 1 +# 11 0.8 1.0 0.0 0.7 Moustakas 2013 +10.800 -2.583 -2.555 -2.609 1 +10.900 -2.658 -2.635 -2.680 1 +11.000 -2.701 -2.673 -2.727 1 +11.100 -2.842 -2.814 -2.868 1 +11.200 -3.039 -3.004 -3.071 1 +11.300 -3.296 -3.257 -3.332 1 +11.400 -3.453 -3.374 -3.521 1 +11.500 -3.770 -3.670 -3.860 1 +11.600 -4.320 -4.220 -4.420 1 +11.700 -4.440 -4.240 -4.640 1 +11.800 -5.070 -4.670 -5.270 1 +# 37 0.2 0.5 0.03 0.7 Muzzin 2013 +8.4 -1.599 -1.520 -1.689 1 +8.5 -1.702 -1.647 -1.758 1 +8.6 -1.708 -1.657 -1.760 1 +8.7 -1.747 -1.697 -1.799 1 +8.8 -1.832 -1.783 -1.882 1 +8.9 -1.860 -1.810 -1.909 1 +9.0 -1.894 -1.844 -1.944 1 +9.1 -1.942 -1.891 -1.992 1 +9.2 -1.978 -1.927 -2.029 1 +9.3 -2.039 -1.983 -2.091 1 +9.4 -2.101 -2.045 -2.154 1 +9.5 -2.111 -2.059 -2.163 1 +9.6 -2.170 -2.114 -2.223 1 +9.7 -2.220 -2.167 -2.273 1 +9.8 -2.211 -2.155 -2.264 1 +9.9 -2.208 -2.153 -2.263 1 +10.0 -2.282 -2.222 -2.337 1 +10.1 -2.319 -2.260 -2.375 1 +10.2 -2.321 -2.261 -2.378 1 +10.3 -2.357 -2.295 -2.416 1 +10.4 -2.341 -2.280 -2.400 1 +10.5 -2.388 -2.326 -2.449 1 +10.6 -2.392 -2.330 -2.454 1 +10.7 -2.466 -2.397 -2.530 1 +10.8 -2.560 -2.489 -2.627 1 +10.9 -2.616 -2.546 -2.687 1 +11.0 -2.716 -2.641 -2.790 1 +11.1 -2.839 -2.758 -2.919 1 +11.2 -3.049 -2.947 -3.140 1 +11.3 -3.069 -2.972 -3.164 1 +11.4 -3.279 -3.171 -3.405 1 +11.5 -3.606 -3.466 -3.741 1 +11.6 -4.226 -3.887 -4.475 1 +11.7 -4.226 -3.997 -4.499 1 +11.8 -4.527 -4.212 -4.911 1 +11.9 -5.004 -4.430 -5.786 1 +12.0 -4.703 -4.317 -5.169 1 +# 29 0.5 1.0 0.03 0.7 Muzzin 2013 +8.9 -2.075 -2.035 -2.111 1 +9.0 -2.068 -2.038 -2.099 1 +9.1 -2.090 -2.059 -2.124 1 +9.2 -2.144 -2.115 -2.180 1 +9.3 -2.129 -2.101 -2.158 1 +9.4 -2.151 -2.122 -2.181 1 +9.5 -2.201 -2.171 -2.230 1 +9.6 -2.260 -2.231 -2.289 1 +9.7 -2.234 -2.205 -2.265 1 +9.8 -2.272 -2.242 -2.301 1 +9.9 -2.307 -2.277 -2.337 1 +10.0 -2.348 -2.316 -2.378 1 +10.1 -2.419 -2.386 -2.450 1 +10.2 -2.367 -2.335 -2.399 1 +10.3 -2.402 -2.369 -2.435 1 +10.4 -2.417 -2.384 -2.450 1 +10.5 -2.514 -2.479 -2.548 1 +10.6 -2.500 -2.464 -2.535 1 +10.7 -2.549 -2.510 -2.585 1 +10.8 -2.631 -2.592 -2.671 1 +10.9 -2.748 -2.707 -2.788 1 +11.0 -2.910 -2.863 -2.953 1 +11.1 -3.015 -2.965 -3.064 1 +11.2 -3.221 -3.161 -3.275 1 +11.3 -3.439 -3.373 -3.500 1 +11.4 -3.704 -3.610 -3.780 1 +11.5 -3.974 -3.879 -4.065 1 +11.6 -4.195 -4.072 -4.322 1 +11.7 -4.593 -4.410 -4.770 1 +# 24 1.0 1.5 0.03 0.7 Muzzin 2013 +9.5 -2.374 -2.320 -2.426 1 +9.6 -2.369 -2.338 -2.402 1 +9.7 -2.378 -2.351 -2.407 1 +9.8 -2.457 -2.429 -2.486 1 +9.9 -2.505 -2.478 -2.531 1 +10.0 -2.540 -2.514 -2.568 1 +10.1 -2.621 -2.593 -2.650 1 +10.2 -2.602 -2.573 -2.634 1 +10.3 -2.637 -2.608 -2.668 1 +10.4 -2.648 -2.619 -2.679 1 +10.5 -2.680 -2.649 -2.711 1 +10.6 -2.703 -2.669 -2.738 1 +10.7 -2.800 -2.766 -2.839 1 +10.8 -2.852 -2.816 -2.888 1 +10.9 -2.962 -2.924 -3.001 1 +11.0 -3.082 -3.041 -3.125 1 +11.1 -3.249 -3.199 -3.298 1 +11.2 -3.466 -3.412 -3.521 1 +11.3 -3.828 -3.750 -3.897 1 +11.4 -4.000 -3.923 -4.086 1 +11.5 -4.434 -4.288 -4.552 1 +11.6 -4.650 -4.485 -4.843 1 +11.7 -5.182 -4.877 -5.528 1 +11.8 -5.881 -5.252 -6.708 1 +# 16 1.5 2.0 0.03 0.7 Muzzin 2013 +10.1 -2.713 -2.678 -2.764 1 +10.2 -2.805 -2.768 -2.861 1 +10.3 -2.830 -2.798 -2.870 1 +10.4 -2.901 -2.868 -2.935 1 +10.5 -2.957 -2.922 -2.997 1 +10.6 -2.954 -2.918 -2.991 1 +10.7 -2.984 -2.946 -3.025 1 +10.8 -3.145 -3.100 -3.193 1 +10.9 -3.158 -3.114 -3.208 1 +11.0 -3.314 -3.260 -3.367 1 +11.1 -3.576 -3.517 -3.633 1 +11.2 -3.733 -3.669 -3.797 1 +11.3 -4.078 -3.980 -4.160 1 +11.4 -4.416 -4.291 -4.531 1 +11.5 -4.717 -4.558 -4.862 1 +11.6 -5.470 -5.004 -5.865 1 +# 13 2.0 2.5 0.03 0.7 Muzzin 2013 +10.5 -3.275 -3.218 -3.332 1 +10.6 -3.298 -3.249 -3.359 1 +10.7 -3.332 -3.282 -3.381 1 +10.8 -3.445 -3.383 -3.502 1 +10.9 -3.549 -3.491 -3.609 1 +11.0 -3.614 -3.548 -3.680 1 +11.1 -3.811 -3.739 -3.889 1 +11.2 -4.014 -3.921 -4.096 1 +11.3 -4.091 -3.994 -4.225 1 +11.4 -4.556 -4.414 -4.683 1 +11.5 -5.255 -4.880 -5.540 1 +11.6 -5.109 -4.872 -5.400 1 +11.7 -5.352 -4.975 -5.876 1 +# 6 2.5 3.0 0.03 0.7 Muzzin 2013 +10.8 -3.454 -3.391 -3.562 1 +11.0 -3.806 -3.733 -3.876 1 +11.2 -4.047 -3.957 -4.135 1 +11.4 -4.351 -4.241 -4.460 1 +11.6 -5.045 -4.813 -5.224 1 +11.8 -5.471 -5.160 -5.824 1 +# 6 3.0 4.0 0.03 0.7 Muzzin 2013 +11.0 -4.361 -4.235 -4.484 1 +11.2 -4.856 -4.637 -4.969 1 +11.4 -4.997 -4.793 -5.137 1 +11.6 -5.206 -5.011 -5.371 1 +11.8 -5.829 -5.457 -6.132 1 +12.0 -6.051 -5.619 -6.446 1 +# 7 3.0 3.5 0.24 0.7 Caputi 2011 +10.400 -3.580 -3.540 -3.630 1 +10.600 -3.690 -3.650 -3.740 1 +10.800 -3.860 -3.810 -3.920 1 +11.000 -4.030 -3.970 -4.100 1 +11.200 -4.220 -4.150 -4.370 1 +11.400 -4.670 -4.560 -4.840 1 +11.600 -5.170 -4.990 -5.470 1 +# 7 3.5 4.25 0.24 0.7 Caputi 2011 +10.400 -3.920 -3.870 -3.980 1 +10.600 -4.050 -3.990 -4.120 1 +10.800 -4.340 -4.200 -4.430 1 +11.000 -4.550 -4.380 -4.670 1 +11.200 -4.660 -4.560 -4.790 1 +11.400 -5.490 -4.960 -6.020 1 +11.600 -5.920 -5.380 -6.680 1 +# 6 4.25 5.0 0.24 0.7 Caputi 2011 +10.400 -4.480 -4.370 -4.710 1 +10.600 -4.910 -4.520 -5.140 1 +10.800 -4.790 -4.650 -5.180 1 +11.000 -5.390 -4.870 -5.920 1 +11.200 -5.460 -4.960 -5.990 1 +11.400 -5.830 -5.230 -6.590 1 +# 11 0.1 0.35 0.0 0.7 Pozzetti 2010 +8.500 -1.759 -1.679 -1.839 1 +8.790 -1.846 -1.803 -1.889 1 +9.080 -1.976 -1.945 -2.007 1 +9.370 -2.070 -2.039 -2.100 1 +9.660 -2.175 -2.144 -2.206 1 +9.950 -2.238 -2.213 -2.262 1 +10.240 -2.263 -2.214 -2.312 1 +10.530 -2.276 -2.239 -2.313 1 +10.820 -2.493 -2.450 -2.536 1 +11.110 -2.987 -2.907 -3.067 1 +11.400 -3.877 -3.710 -4.043 1 +# 7 0.35 0.55 0.0 0.7 Pozzetti 2010 +9.700 -2.146 -2.059 -2.233 1 +9.980 -2.350 -2.295 -2.406 1 +10.260 -2.400 -2.357 -2.444 1 +10.540 -2.463 -2.432 -2.494 1 +10.820 -2.587 -2.544 -2.630 1 +11.100 -3.051 -2.989 -3.113 1 +11.380 -4.171 -4.017 -4.326 1 +# 5 0.55 0.75 0.0 0.7 Pozzetti 2010 +10.250 -2.494 -2.438 -2.549 1 +10.540 -2.500 -2.457 -2.543 1 +10.830 -2.648 -2.605 -2.691 1 +11.120 -2.944 -2.889 -3.000 1 +11.410 -3.747 -3.660 -3.833 1 +# 3 0.75 1.0 0.0 0.7 Pozzetti 2010 +10.840 -2.778 -2.728 -2.827 1 +11.130 -3.056 -3.012 -3.099 1 +11.420 -3.796 -3.722 -3.870 1 +# 8 1.3 2.0 0.03 0.7 Marchesini 2009 +9.53 -2.608 -2.319 -2.900 1 +9.83 -2.730 -2.485 -2.975 1 +10.13 -2.843 -2.737 -2.949 1 +10.43 -2.761 -2.685 -2.837 1 +10.73 -2.892 -2.782 -3.002 1 +11.03 -3.189 -3.122 -3.256 1 +11.33 -3.745 -3.577 -3.913 1 +11.63 -4.817 -4.534 -5.117 1 +# 7 2.0 3.0 0.03 0.7 Marchesini 2009 +9.89 -2.640 -2.34 -2.942 1 +10.18 -3.401 -3.110 -3.699 1 +10.47 -3.119 -2.904 -3.334 1 +10.76 -3.402 -3.284 -3.520 1 +11.05 -3.570 -3.467 -3.673 1 +11.34 -3.949 -3.836 -4.062 1 +11.63 -5.067 -4.734 -5.423 1 +# 6 3.0 4.0 0.03 0.7 Marchesini 2009 +10.21 -3.141 -3.637 -2.617 1 +10.50 -3.433 -3.886 -2.963 1 +10.79 -3.929 -4.353 -3.498 1 +11.08 -4.025 -4.242 -3.804 1 +11.37 -4.282 -4.604 -3.953 1 +11.66 -4.784 -5.230 -4.331 1 +# 14 0.5 1.0 0.24 0.7 Kajisawa 2009 +8.75 -1.768 -1.758 -1.778 1 +9.00 -1.834 -1.824 -1.844 1 +9.25 -1.883 -1.873 -1.893 1 +9.50 -1.950 -1.940 -1.960 1 +9.75 -2.064 -2.054 -2.074 1 +10.00 -2.196 -2.186 -2.206 1 +10.25 -2.245 -2.235 -2.255 1 +10.50 -2.344 -2.334 -2.354 1 +10.75 -2.361 -2.351 -2.371 1 +11.00 -2.525 -2.515 -2.575 1 +11.25 -2.705 -2.655 -2.805 1 +11.50 -3.291 -3.091 -3.391 1 +11.75 -4.040 -3.540 -4.240 1 +12.00 -4.350 -3.350 -4.650 1 +# 10 1.0 1.5 0.24 0.7 Kajisawa 2009 +9.50 -2.050 -2.040 -2.060 1 +9.75 -2.212 -2.202 -2.222 1 +10.00 -2.439 -2.429 -2.449 1 +10.25 -2.519 -2.509 -2.529 1 +10.50 -2.632 -2.622 -2.642 1 +10.75 -2.663 -2.653 -2.673 1 +11.00 -2.906 -2.896 -2.956 1 +11.25 -3.084 -3.034 -3.184 1 +11.50 -3.685 -3.585 -3.885 1 +11.75 -4.220 -3.220 -4.520 1 +# 10 1.5 2.5 0.24 0.7 Kajisawa 2009 +9.50 -2.208 -2.198 -2.218 1 +9.75 -2.404 -2.394 -2.414 1 +10.00 -2.551 -2.541 -2.561 1 +10.25 -2.682 -2.672 -2.692 1 +10.50 -2.796 -2.786 -2.806 1 +10.75 -2.943 -2.933 -2.953 1 +11.00 -3.204 -3.194 -3.254 1 +11.25 -3.416 -3.366 -3.516 1 +11.50 -4.020 -3.920 -4.220 1 +11.75 -4.445 -4.245 -4.745 1 +# 6 2.5 3.5 0.24 0.7 Kajisawa 2009 +10.25 -2.879 -2.869 -2.889 1 +10.50 -3.157 -3.147 -3.167 1 +10.75 -3.369 -3.359 -3.419 1 +11.00 -3.988 -3.938 -4.088 1 +11.25 -3.827 -3.727 -3.927 1 +11.50 -4.250 -4.150 -4.550 1 +# 14 1.0 1.5 0.24 0.7 Mortlock 2011 +8.60 -1.790 -1.670 -1.910 1 +8.85 -1.830 -1.710 -1.950 1 +9.10 -1.990 -1.880 -2.100 1 +9.35 -2.060 -1.940 -2.180 1 +9.60 -2.310 -2.180 -2.440 1 +9.85 -2.400 -2.280 -2.520 1 +10.10 -2.480 -2.350 -2.610 1 +10.35 -2.600 -2.470 -2.730 1 +10.60 -2.670 -2.540 -2.800 1 +10.85 -2.550 -2.410 -2.690 1 +11.10 -2.890 -2.740 -3.040 1 +11.35 -3.170 -3.020 -3.320 1 +11.60 -3.470 -3.290 -3.650 1 +11.85 -4.170 -3.930 -4.410 1 +# 12 1.5 2.0 0.24 0.7 Mortlock 2011 +9.35 -2.380 -2.260 -2.500 1 +9.60 -2.370 -2.240 -2.500 1 +9.85 -2.540 -2.420 -2.660 1 +10.10 -2.560 -2.430 -2.690 1 +10.35 -2.570 -2.440 -2.700 1 +10.60 -2.640 -2.510 -2.770 1 +10.85 -2.680 -2.550 -2.810 1 +11.10 -3.020 -2.880 -3.160 1 +11.35 -3.040 -2.890 -3.190 1 +11.60 -3.550 -3.350 -3.750 1 +11.85 -3.950 -3.700 -4.200 1 +12.10 -4.250 -3.980 -4.520 1 +# 11 2.0 2.5 0.24 0.7 Mortlock 2011 +9.35 -2.170 -2.050 -2.290 1 +9.60 -2.150 -2.030 -2.270 1 +9.85 -2.210 -2.090 -2.330 1 +10.10 -2.320 -2.190 -2.450 1 +10.35 -2.620 -2.490 -2.750 1 +10.60 -2.700 -2.570 -2.830 1 +10.85 -2.890 -2.760 -3.020 1 +11.10 -3.450 -3.030 -3.870 1 +11.35 -3.640 -3.180 -4.100 1 +11.60 -3.980 -3.440 -4.520 1 +11.85 -4.750 -2.940 -6.560 1 +# 10 2.5 3.0 0.24 0.7 Mortlock 2011 +9.60 -2.070 -1.940 -2.200 1 +9.85 -2.210 -2.090 -2.330 1 +10.10 -2.310 -2.180 -2.440 1 +10.35 -2.680 -2.540 -2.820 1 +10.60 -3.010 -2.870 -3.150 1 +10.85 -3.090 -2.950 -3.230 1 +11.10 -3.850 -3.380 -4.320 1 +11.35 -3.980 -3.490 -4.470 1 +11.60 -4.150 -3.620 -4.680 1 +11.85 -4.750 -3.940 -5.560 1 +# 11 3.0 3.5 0.24 0.7 Mortlock 2011 +9.60 -2.450 -2.330 -2.570 1 +9.85 -2.560 -2.430 -2.690 1 +10.10 -2.620 -2.490 -2.750 1 +10.35 -2.880 -2.740 -3.020 1 +10.60 -2.980 -2.840 -3.120 1 +10.85 -3.140 -2.980 -3.300 1 +11.10 -3.960 -3.760 -4.160 1 +11.35 -3.780 -3.610 -3.950 1 +11.60 -3.960 -3.770 -4.150 1 +11.85 -3.960 -3.770 -4.150 1 +12.10 -3.960 -3.740 -4.180 1 +# 10 3.5 4.0 0.0 0.72 Lee 2012 +8.87 -2.507 -2.372 -2.665 1 +9.12 -2.702 -2.612 -2.807 1 +9.37 -2.844 -2.747 -2.942 1 +9.62 -2.881 -2.799 -2.964 1 +9.87 -3.099 -3.016 -3.181 1 +10.12 -3.361 -3.278 -3.443 1 +10.37 -3.683 -3.541 -3.878 1 +10.62 -4.223 -4.021 -4.569 1 +10.87 -4.778 -4.508 -5.364 1 +11.12 -4.785 -4.448 -6.370 1 +# 9 4.5 5.5 0.0 0.72 Lee 2012 +8.87 -3.161 -3.026 -3.296 1 +9.12 -3.303 -3.176 -3.446 1 +9.37 -3.416 -3.251 -3.641 1 +9.62 -3.484 -3.327 -3.694 1 +9.87 -3.635 -3.507 -3.755 1 +10.12 -3.823 -3.695 -3.950 1 +10.37 -4.250 -4.093 -4.423 1 +10.62 -4.386 -4.198 -4.656 1 +10.87 -5.001 -4.626 -6.455 1 +# 7 3.4 4.2 0.24 0.7 Gonzalez 2011 +7.75 -1.90 -1.79 -2.02 1 +8.25 -1.96 -1.84 -2.08 1 +8.75 -2.30 -2.20 -2.41 1 +9.25 -2.53 -2.43 -2.64 1 +9.75 -3.14 -3.02 -3.27 1 +10.25 -3.80 -3.60 -4.03 1 +10.75 -4.43 -4.17 -4.89 1 +# 7 4.5 5.5 0.24 0.7 Gonzalez 2011 +7.75 -2.21 -2.03 -2.41 1 +8.25 -2.27 -2.09 -2.47 1 +8.75 -2.60 -2.45 -2.79 1 +9.25 -2.84 -2.67 -3.01 1 +9.75 -3.46 -3.29 -3.67 1 +10.25 -4.12 -3.90 -4.39 1 +10.75 -4.81 -4.48 -5.26 1 +# 7 5.5 6.3 0.24 0.7 Gonzalez 2011 +7.75 -2.09 -1.86 -2.33 1 +8.25 -2.16 -1.93 -2.39 1 +8.75 -2.55 -2.35 -2.76 1 +9.25 -2.81 -2.62 -3.03 1 +9.75 -3.52 -3.31 -3.75 1 +10.25 -4.22 -3.94 -4.53 1 +10.75 -4.97 -4.59 -5.67 1 +# 6 6.3 7.3 0.24 0.7 Gonzalez 2011 +7.75 -2.15 -1.74 -2.54 1 +8.25 -2.23 -1.86 -2.61 1 +8.75 -2.70 -2.36 -3.06 1 +9.25 -3.01 -2.67 -3.35 1 +9.75 -3.80 -3.44 -4.17 1 +10.25 -4.53 -4.14 -5.14 1 +# 10 3.8 4.2 0.0 0.7 Duncan 2014 +8.63 -1.771 -1.662 -1.881 1 +8.88 -1.918 -1.809 -2.028 1 +9.13 -2.120 -2.010 -2.230 1 +9.38 -2.386 -2.267 -2.487 1 +9.63 -2.615 -2.496 -2.716 1 +9.88 -2.972 -2.854 -3.110 1 +10.13 -3.220 -3.110 -3.357 1 +10.38 -3.477 -3.358 -3.696 1 +10.63 -4.090 -3.971 -4.419 1 +10.88 -4.465 -4.200 -5.343 1 +# 9 4.8 5.2 0.0 0.7 Duncan 2014 +8.71 -1.967 -1.820 -2.206 1 +9.02 -2.323 -2.204 -2.470 1 +9.33 -2.588 -2.469 -2.735 1 +9.64 -2.825 -2.706 -2.954 1 +9.95 -2.943 -2.823 -3.191 1 +10.26 -3.336 -3.189 -3.602 1 +10.57 -3.876 -3.711 -4.088 1 +10.88 -4.371 -4.113 -5.023 1 +11.19 -5.214 -4.599 -6.060 1 +# 7 5.8 6.2 0.0 0.7 Duncan 2014 +8.97 -2.564 -2.418 -2.693 1 +9.32 -2.921 -2.793 -3.077 1 +9.67 -3.342 -3.223 -3.507 1 +10.02 -3.644 -3.506 -3.846 1 +10.37 -3.964 -3.734 -4.551 1 +10.72 -4.797 -4.403 -6.137 1 +11.07 -5.723 -4.475 -6.998 1 +# 4 6.8 7.2 0.0 0.7 Duncan 2014 +9.3 -3.011 -2.744 -3.737 1 +9.66 -3.413 -3.119 -5.601 1 +10.02 -3.714 -3.328 -6.389 1 +10.38 -4.411 -3.436 -6.993 1 +# 9 3.5 4.5 0.24 0.7 Song 2009 +7.250 -1.570 -1.360 -1.730 1 +7.750 -1.770 -1.620 -1.910 1 +8.250 -2.000 -1.870 -2.100 1 +8.750 -2.220 -2.130 -2.310 1 +9.250 -2.520 -2.430 -2.610 1 +9.750 -2.910 -2.790 -2.960 1 +10.250 -3.370 -3.280 -3.490 1 +10.750 -4.000 -3.800 -4.250 1 +11.250 -4.540 -4.200 -5.090 1 +# 9 4.5 5.5 0.24 0.7 Song 2009 +7.250 -1.470 -1.230 -1.680 1 +7.750 -1.720 -1.520 -1.920 1 +8.250 -2.010 -1.850 -2.170 1 +8.750 -2.330 -2.180 -2.430 1 +9.250 -2.680 -2.610 -2.820 1 +9.750 -3.120 -3.030 -3.230 1 +10.250 -3.470 -3.310 -3.610 1 +10.750 -4.120 -3.870 -4.500 1 +11.250 -4.880 -4.480 -5.490 1 +# 7 5.5 6.5 0.24 0.7 Song 2009 +7.250 -1.470 -1.120 -1.790 1 +7.750 -1.810 -1.580 -2.090 1 +8.250 -2.260 -2.050 -2.420 1 +8.750 -2.650 -2.500 -2.800 1 +9.250 -3.140 -3.020 -3.250 1 +9.750 -3.690 -3.570 -3.820 1 +10.250 -4.270 -3.890 -5.130 1 +# 8 6.5 7.5 0.24 0.7 Song 2009 +7.250 -1.630 -1.090 -2.170 1 +7.750 -2.070 -1.620 -2.480 1 +8.250 -2.490 -2.110 -2.810 1 +8.750 -2.960 -2.640 -3.260 1 +9.250 -3.470 -3.150 -3.820 1 +9.750 -4.110 -3.700 -4.680 1 +10.250 -4.610 -3.890 -5.430 1 +10.750 -5.240 -4.340 -5.810 1 +# 6 7.5 8.5 0.24 0.7 Song 2009 +7.250 -1.730 -0.720 -2.570 1 +7.750 -2.280 -1.440 -2.920 1 +8.250 -2.880 -2.130 -3.450 1 +8.750 -3.450 -2.880 -4.050 1 +9.250 -4.210 -3.580 -4.990 1 +9.750 -5.310 -4.300 -6.950 1 +# 14 3.5 4.5 0.24 0.7 Grazian 2015 +8.930 -1.948 -1.694 -2.150 1 +9.130 -2.208 -2.042 -2.363 1 +9.330 -2.410 -2.244 -2.555 1 +9.530 -2.550 -2.399 -2.695 1 +9.730 -2.804 -2.649 -2.955 1 +9.930 -2.965 -2.815 -3.110 1 +10.130 -3.141 -2.996 -3.281 1 +10.330 -3.375 -3.214 -3.505 1 +10.530 -3.541 -3.370 -3.660 1 +10.730 -3.863 -3.691 -3.982 1 +10.930 -4.195 -4.049 -4.340 1 +11.130 -4.392 -4.169 -4.589 1 +11.330 -4.853 -4.480 -5.258 1 +11.730 -5.336 -4.776 -6.114 1 +# 13 4.5 5.5 0.24 0.7 Grazian 2015 +9.320 -2.810 -2.504 -3.105 1 +9.520 -2.830 -2.675 -2.986 1 +9.720 -3.058 -2.913 -3.203 1 +9.920 -3.140 -2.985 -3.285 1 +10.120 -3.300 -3.155 -3.435 1 +10.320 -3.616 -3.481 -3.761 1 +10.520 -3.755 -3.595 -3.880 1 +10.720 -4.081 -3.941 -4.226 1 +10.920 -4.334 -4.106 -4.526 1 +11.120 -4.598 -4.267 -4.904 1 +11.320 -4.981 -4.546 -5.489 1 +11.520 -5.291 -4.721 -6.084 1 +11.720 -5.286 -4.716 -6.037 1 +# 8 5.5 6.5 0.24 0.7 Grazian 2015 +9.500 -2.771 -2.444 -3.051 1 +9.700 -3.264 -3.072 -3.383 1 +9.900 -3.346 -3.175 -3.476 1 +10.100 -3.616 -3.455 -3.751 1 +10.300 -3.839 -3.662 -3.943 1 +10.500 -4.347 -4.119 -4.549 1 +10.700 -4.658 -4.321 -4.964 1 +10.900 -4.953 -4.492 -5.446 1 +# 6 6.5 7.5 0.24 0.7 Grazian 2015 +9.750 -3.487 -3.088 -3.778 1 +9.950 -4.343 -3.933 -4.820 1 +10.150 -4.254 -3.989 -4.487 1 +10.350 -4.875 -4.466 -5.353 1 +10.550 -5.207 -4.610 -6.000 1 +10.750 -5.185 -4.614 -5.984 1 +END diff --git a/data/ssfr.dat b/data/ssfr.dat new file mode 100644 index 0000000..85d1d32 --- /dev/null +++ b/data/ssfr.dat @@ -0,0 +1,281 @@ +# Header: N IMF hubble Source +# 7 0.0 0.7 Salim 2007 UV +0.1 8.50 -9.4 0.3 0.3 1 +0.1 9.00 -9.5 0.3 0.3 1 +0.1 9.50 -9.7 0.3 0.3 1 +0.1 10.0 -9.9 0.3 0.3 1 +0.1 10.5 -10.2 0.3 0.3 1 +0.1 11.0 -10.6 0.3 0.3 1 +0.1 11.5 -11.6 0.01 0.01 1 +# 16 0.0 0.7 Zheng 2007 UV/IR +0.3 9.250 -9.45 0.3 0.3 1 +0.3 9.750 -9.61 0.3 0.3 1 +0.3 10.25 -9.77 0.3 0.3 1 +0.3 10.75 -10.17 0.3 0.3 1 +0.5 9.250 -9.15 0.3 0.3 1 +0.5 9.750 -9.29 0.3 0.3 1 +0.5 10.25 -9.55 0.3 0.3 1 +0.5 10.75 -9.87 0.3 0.3 1 +0.7 9.250 -8.95 0.3 0.3 1 +0.7 9.750 -9.01 0.3 0.3 1 +0.7 10.25 -9.32 0.3 0.3 1 +0.7 10.75 -9.65 0.3 0.3 1 +0.9 9.250 -8.79 0.3 0.3 1 +0.9 9.750 -8.84 0.3 0.3 1 +0.9 10.25 -9.07 0.3 0.3 1 +0.9 10.75 -9.58 0.3 0.3 1 +# 1 0.0 0.7 Twite 2012 Ha +1.0 11.1 -9.2 0.3 0.9 1 +# 46 0.03 0.7 Noeske 2007 +0.975 9.60 -8.49 0.3 0.3 1 +0.975 9.80 -8.79 0.3 0.3 1 +0.975 10.0 -8.91 0.3 0.3 1 +0.975 10.2 -9.11 0.3 0.3 1 +0.975 10.4 -9.23 0.3 0.3 1 +0.975 10.6 -9.34 0.3 0.3 1 +0.975 10.8 -9.45 0.3 0.3 1 +0.975 11.0 -9.53 0.3 0.3 1 +0.975 11.2 -9.66 0.3 0.3 1 +0.975 11.4 -9.82 0.3 0.3 1 +0.975 11.6 -10.13 0.3 0.3 1 +0.775 9.40 -8.75 0.3 0.3 1 +0.775 9.60 -8.93 0.3 0.3 1 +0.775 9.80 -9.04 0.3 0.3 1 +0.775 10.0 -9.23 0.3 0.3 1 +0.775 10.2 -9.34 0.3 0.3 1 +0.775 10.4 -9.48 0.3 0.3 1 +0.775 10.6 -9.50 0.3 0.3 1 +0.775 10.8 -9.60 0.3 0.3 1 +0.775 11.0 -9.70 0.3 0.3 1 +0.775 11.2 -9.90 0.3 0.3 1 +0.575 9.00 -8.76 0.3 0.3 1 +0.575 9.20 -8.86 0.3 0.3 1 +0.575 9.40 -9.02 0.3 0.3 1 +0.575 9.60 -9.16 0.3 0.3 1 +0.575 9.80 -9.25 0.3 0.3 1 +0.575 10.0 -9.31 0.3 0.3 1 +0.575 10.2 -9.44 0.3 0.3 1 +0.575 10.4 -9.55 0.3 0.3 1 +0.575 10.6 -9.67 0.3 0.3 1 +0.575 10.8 -9.75 0.3 0.3 1 +0.575 11.0 -9.89 0.3 0.3 1 +0.575 11.2 -10.12 0.3 0.3 1 +0.325 8.80 -9.23 0.3 0.3 1 +0.325 9.00 -9.30 0.3 0.3 1 +0.325 9.20 -9.41 0.3 0.3 1 +0.325 9.40 -9.47 0.3 0.3 1 +0.325 9.60 -9.52 0.3 0.3 1 +0.325 9.80 -9.56 0.3 0.3 1 +0.325 10.0 -9.65 0.3 0.3 1 +0.325 10.2 -9.73 0.3 0.3 1 +0.325 10.4 -9.81 0.3 0.3 1 +0.325 10.6 -9.96 0.3 0.3 1 +0.325 10.8 -10.07 0.3 0.3 1 +0.325 11.0 -10.21 0.3 0.3 1 +0.325 11.2 -10.12 0.3 0.3 1 +# 1 0.24 0.7 Tadaki 2011 +2.2 10.6 -9.16 0.3 0.3 1 +# 60 0.0 0.7 Whitaker 2012 +0.25 8.58 -9.31 0.3 0.3 1 +0.25 8.69 -9.29 0.3 0.3 1 +0.25 8.80 -9.37 0.3 0.3 1 +0.25 8.89 -9.38 0.3 0.3 1 +0.25 8.99 -9.38 0.3 0.3 1 +0.25 9.08 -9.43 0.3 0.3 1 +0.25 9.18 -9.45 0.3 0.3 1 +0.25 9.29 -9.49 0.3 0.3 1 +0.25 9.41 -9.48 0.3 0.3 1 +0.25 9.54 -9.53 0.3 0.3 1 +0.25 9.70 -9.52 0.3 0.3 1 +0.25 9.93 -9.65 0.3 0.3 1 +0.25 10.19 -9.65 0.3 0.3 1 +0.25 10.57 -9.90 0.3 0.3 1 +0.75 8.99 -8.87 0.3 0.3 1 +0.75 9.18 -8.93 0.3 0.3 1 +0.75 9.30 -8.99 0.3 0.3 1 +0.75 9.39 -9.00 0.3 0.3 1 +0.75 9.49 -9.04 0.3 0.3 1 +0.75 9.58 -9.02 0.3 0.3 1 +0.75 9.68 -9.06 0.3 0.3 1 +0.75 9.79 -9.05 0.3 0.3 1 +0.75 9.90 -9.07 0.3 0.3 1 +0.75 10.04 -9.08 0.3 0.3 1 +0.75 10.21 -9.18 0.3 0.3 1 +0.75 10.42 -9.25 0.3 0.3 1 +0.75 10.75 -9.45 0.3 0.3 1 +1.25 9.39 -8.66 0.3 0.3 1 +1.25 9.56 -8.73 0.3 0.3 1 +1.25 9.66 -8.77 0.3 0.3 1 +1.25 9.76 -8.80 0.3 0.3 1 +1.25 9.86 -8.82 0.3 0.3 1 +1.25 9.95 -8.80 0.3 0.3 1 +1.25 10.07 -8.84 0.3 0.3 1 +1.25 10.18 -8.84 0.3 0.3 1 +1.25 10.32 -8.90 0.3 0.3 1 +1.25 10.46 -8.98 0.3 0.3 1 +1.25 10.62 -9.04 0.3 0.3 1 +1.25 10.91 -9.10 0.3 0.3 1 +1.75 9.73 -8.57 0.3 0.3 1 +1.75 9.89 -8.58 0.3 0.3 1 +1.75 10.01 -8.57 0.3 0.3 1 +1.75 10.12 -8.57 0.3 0.3 1 +1.75 10.22 -8.58 0.3 0.3 1 +1.75 10.33 -8.60 0.3 0.3 1 +1.75 10.45 -8.68 0.3 0.3 1 +1.75 10.55 -8.69 0.3 0.3 1 +1.75 10.67 -8.82 0.3 0.3 1 +1.75 10.83 -8.86 0.3 0.3 1 +1.75 11.06 -9.01 0.3 0.3 1 +2.25 9.89 -8.44 0.3 0.3 1 +2.25 10.10 -8.57 0.3 0.3 1 +2.25 10.22 -8.50 0.3 0.3 1 +2.25 10.34 -8.52 0.3 0.3 1 +2.25 10.45 -8.53 0.3 0.3 1 +2.25 10.57 -8.54 0.3 0.3 1 +2.25 10.67 -8.56 0.3 0.3 1 +2.25 10.83 -8.68 0.3 0.3 1 +2.25 11.00 -8.78 0.3 0.3 1 +2.25 11.25 -8.90 0.3 0.3 1 +# 8 0.24 0.73 Daddi 2007 +2.0 9.60 -8.56 0.3 0.3 1 +2.0 9.80 -8.58 0.3 0.3 1 +2.0 10.0 -8.60 0.3 0.3 1 +2.0 10.2 -8.62 0.3 0.3 1 +2.0 10.4 -8.64 0.3 0.3 1 +2.0 10.6 -8.66 0.3 0.3 1 +2.0 10.8 -8.68 0.3 0.3 1 +2.0 11.0 -8.70 0.3 0.3 1 +# 7 0.0 0.7 Salmi 2012 +0.9 9.80 -9.31 0.3 0.3 1 +0.9 10.0 -9.31 0.3 0.3 1 +0.9 10.2 -9.32 0.3 0.3 1 +0.9 10.4 -9.32 0.3 0.3 1 +0.9 10.6 -9.32 0.3 0.3 1 +0.9 10.8 -9.33 0.3 0.3 1 +0.9 11.0 -9.33 0.3 0.3 1 +# 31 0.0 0.7 Karim 2011 +0.27 10.04 -9.96 0.07 0.08 1 +0.49 10.05 -9.56 0.08 0.09 1 +0.29 10.35 -10.15 0.05 0.03 1 +0.49 10.34 -9.71 0.05 0.05 1 +0.68 10.35 -9.59 0.09 0.10 1 +0.89 10.35 -9.33 0.07 0.08 1 +1.09 10.35 -9.13 0.06 0.06 1 +0.28 10.63 -10.37 0.05 0.05 1 +0.48 10.64 -9.96 0.07 0.05 1 +0.69 10.63 -9.73 0.05 0.10 1 +0.89 10.64 -9.55 0.05 0.09 1 +1.09 10.64 -9.34 0.06 0.07 1 +1.37 10.64 -9.10 0.07 0.08 1 +1.78 10.63 -8.81 0.06 0.04 1 +0.27 10.95 -10.61 0.07 0.09 1 +0.48 10.92 -10.16 0.05 0.04 1 +0.69 10.92 -9.96 0.08 0.09 1 +0.90 10.91 -9.75 0.06 0.04 1 +1.10 10.92 -9.53 0.05 0.06 1 +1.36 10.91 -9.30 0.07 0.09 1 +1.79 10.92 -8.96 0.04 0.03 1 +2.21 10.93 -8.82 0.06 0.05 1 +0.27 11.20 -10.80 0.06 0.17 1 +0.48 11.23 -10.47 0.14 0.38 1 +0.69 11.20 -10.04 0.10 0.13 1 +0.90 11.20 -9.93 0.08 0.12 1 +1.10 11.20 -9.61 0.05 0.07 1 +1.35 11.20 -9.44 0.07 0.11 1 +1.78 11.20 -8.99 0.04 0.06 1 +2.22 11.22 -8.85 0.07 0.07 1 +2.71 11.23 -8.69 0.07 0.07 1 +# 45 0.24 0.7 Kajisawa 2010 +0.75 8.50 -9.08 0.3 0.3 1 +0.75 8.75 -9.13 0.3 0.3 1 +0.75 9.00 -9.15 0.3 0.3 1 +0.75 9.26 -9.19 0.3 0.3 1 +0.75 9.50 -9.13 0.3 0.3 1 +0.75 9.79 -9.25 0.3 0.3 1 +0.75 10.05 -9.34 0.3 0.3 1 +0.75 10.26 -9.42 0.3 0.3 1 +0.75 10.48 -9.26 0.3 0.3 1 +0.75 10.74 -9.52 0.3 0.3 1 +0.75 11.00 -10.36 0.3 0.3 1 +0.75 11.29 -10.37 0.3 0.3 1 +0.75 11.56 -10.36 0.3 0.3 1 +0.75 11.77 -10.34 0.3 0.3 1 +1.25 9.25 -8.92 0.3 0.3 1 +1.25 9.53 -8.93 0.3 0.3 1 +1.25 9.79 -8.76 0.3 0.3 1 +1.25 9.99 -8.52 0.3 0.3 1 +1.25 10.27 -8.62 0.3 0.3 1 +1.25 10.52 -8.58 0.3 0.3 1 +1.25 10.77 -8.66 0.3 0.3 1 +1.25 11.02 -8.93 0.3 0.3 1 +1.25 11.26 -8.87 0.3 0.3 1 +1.25 11.52 -8.98 0.3 0.3 1 +1.25 11.74 -9.02 0.3 0.3 1 +2.00 8.75 -8.89 0.3 0.3 1 +2.00 9.03 -8.95 0.3 0.3 1 +2.00 9.26 -9.01 0.3 0.3 1 +2.00 9.52 -8.87 0.3 0.3 1 +2.00 9.78 -8.89 0.3 0.3 1 +2.00 10.02 -8.86 0.3 0.3 1 +2.00 10.25 -8.75 0.3 0.3 1 +2.00 10.51 -8.94 0.3 0.3 1 +2.00 10.74 -9.45 0.3 0.3 1 +2.00 10.99 -9.29 0.3 0.3 1 +2.00 11.23 -9.75 0.3 0.3 1 +2.00 11.50 -9.80 0.3 0.3 1 +2.00 11.75 -9.85 0.3 0.3 1 +3.00 9.51 -8.82 0.3 0.3 1 +3.00 9.77 -8.95 0.3 0.3 1 +3.00 10.03 -8.85 0.3 0.3 1 +3.00 10.50 -8.76 0.3 0.3 1 +3.00 10.76 -8.48 0.3 0.3 1 +3.00 11.01 -8.96 0.3 0.3 1 +3.00 11.25 -8.94 0.3 0.3 1 +# 8 0.24 0.7 Reddy 2012 +2.3 9.40 -8.61 0.3 1.0 1 +2.3 9.66 -8.69 0.3 0.7 1 +2.3 9.91 -8.82 0.3 0.3 1 +2.3 10.15 -8.52 0.3 0.3 1 +2.3 10.40 -8.82 0.3 0.3 1 +2.3 10.66 -8.61 0.3 0.3 1 +2.3 10.91 -8.45 0.3 0.3 1 +2.3 11.15 -8.69 0.3 0.3 1 +# 18 0.24 0.7 Feulner 2006 +0.60 9.00 -8.60 0.4 0.4 1 +0.60 10.0 -9.20 0.3 0.3 1 +0.60 11.0 -10.11 0.5 0.5 1 +1.00 9.00 -8.60 0.2 0.2 1 +1.00 10.0 -9.09 0.3 0.3 1 +1.00 11.0 -10.04 0.4 0.4 1 +1.50 9.00 -8.59 0.2 0.2 1 +1.50 10.0 -9.07 0.2 0.2 1 +1.50 11.0 -10.02 0.5 0.5 1 +2.15 9.00 -8.41 0.3 0.3 1 +2.15 10.0 -8.84 0.3 0.3 1 +2.15 11.0 -9.56 0.4 0.4 1 +3.00 9.00 -8.38 0.3 0.3 1 +3.00 10.0 -8.81 0.2 0.2 1 +3.00 11.0 -9.21 0.5 0.5 1 +4.25 9.00 -8.25 0.4 0.4 1 +4.25 10.0 -8.78 0.4 0.4 1 +4.25 11.0 -9.20 0.5 0.5 1 +# 6 0.24 0.7 Gonzalez 2012 +4.0 9.0 -8.39 0.3 0.3 1 +4.0 9.7 -8.49 0.3 0.3 1 +5.0 9.0 -8.27 0.3 0.3 1 +5.0 9.7 -8.50 0.3 0.3 1 +6.0 9.0 -8.27 0.3 0.3 1 +6.0 9.7 -8.35 0.3 0.3 1 +# 2 0.24 0.7 Schaerer 2010 +7.0 8.0 -7.97 0.3 0.3 1 +7.0 9.0 -8.17 0.3 0.3 1 +# 1 0.24 0.7 Labbe 2013 +8.0 8.95 -7.95 0.3 0.3 1 +# 1 0.0 0.7 McLure 2011 +6.8 8.92 -8.32 0.3 0.3 1 +# 4 0.0 0.7 Duncan 2014 +4.0 9.7 -8.63 0.25 0.25 1 +5.0 9.7 -8.54 0.25 0.25 1 +6.0 9.7 -8.38 0.25 0.25 1 +7.0 9.7 -8.22 0.25 0.25 1 +END diff --git a/data/wp.dat b/data/wp.dat new file mode 100644 index 0000000..f249bd1 --- /dev/null +++ b/data/wp.dat @@ -0,0 +1,389 @@ +# Redshift for all correlation functions +0.08 +# Header: N mmin mmax imf hubble Comment +# 25 9.0 9.5 10.0 0.0 1.0 Li 2006 +0.11 411.3 133.7 1 +0.14 428.6 94.3 1 +0.18 340.3 76.1 1 +0.23 233.1 52.9 1 +0.28 235.0 37.2 1 +0.36 159.0 24.5 1 +0.45 133.4 17.7 1 +0.57 97.9 15.1 1 +0.71 77.4 11.0 1 +0.90 62.3 9.1 1 +1.13 44.6 7.3 1 +1.42 30.3 5.5 1 +1.79 24.7 4.0 1 +2.25 26.7 4.1 1 +2.84 19.9 3.4 1 +3.57 13.9 3.2 1 +4.50 13.7 2.8 1 +5.66 7.3 2.5 1 +7.13 3.9 2.4 1 +8.97 3.7 1.9 1 +11.29 3.3 1.9 1 +14.22 4.8 1.7 1 +17.90 4.6 1.9 1 +22.54 0.9 1.8 1 +28.37 0.5 1.8 1 +# 25 9.5 10.0 10.0 0.0 1.0 Li 2006 +0.11 432.9 77.7 1 +0.14 393.3 57.2 1 +0.18 355.6 44.9 1 +0.23 218.8 27.9 1 +0.28 209.5 23.5 1 +0.36 174.5 19.6 1 +0.45 151.8 12.9 1 +0.57 141.4 12.1 1 +0.71 107.7 9.8 1 +0.90 85.7 7.9 1 +1.13 64.6 5.9 1 +1.42 45.5 4.7 1 +1.79 37.6 3.9 1 +2.25 29.7 3.4 1 +2.84 23.7 2.4 1 +3.57 20.6 2.2 1 +4.50 18.1 2.1 1 +5.66 15.1 1.9 1 +7.13 10.8 1.7 1 +8.97 9.1 1.6 1 +11.29 7.8 1.8 1 +14.22 3.9 1.6 1 +17.90 1.9 1.4 1 +22.54 2.2 1.4 1 +28.37 3.2 1.3 1 +# 27 10.0 10.5 10.0 0.0 1.0 Li 2006 +0.11 416.0 34.9 1 +0.14 338.8 28.8 1 +0.18 332.3 22.2 1 +0.23 252.8 16.1 1 +0.28 220.6 13.9 1 +0.36 195.2 9.7 1 +0.45 176.3 9.4 1 +0.57 136.5 6.1 1 +0.71 117.2 4.5 1 +0.90 95.0 4.3 1 +1.13 73.3 3.7 1 +1.42 56.8 2.6 1 +1.79 47.9 2.2 1 +2.25 35.9 1.7 1 +2.84 30.1 1.5 1 +3.57 23.1 1.3 1 +4.50 22.6 1.3 1 +5.66 15.6 4.1 1 +7.13 14.3 1.1 1 +8.97 14.0 0.8 1 +11.29 11.9 0.9 1 +14.22 6.6 0.8 1 +17.90 5.9 0.8 1 +22.54 4.5 0.7 1 +28.37 3.2 0.8 1 +35.72 3.1 0.7 1 +44.96 2.4 1.0 1 +# 27 10.5 11.0 10.0 0.0 1.0 Li 2006 +0.11 457.4 29.1 1 +0.14 376.9 24.1 1 +0.18 325.2 19.2 1 +0.23 260.1 12.6 1 +0.28 221.4 10.4 1 +0.36 196.8 8.5 1 +0.45 160.1 6.3 1 +0.57 135.2 4.5 1 +0.71 115.1 3.9 1 +0.90 93.7 3.3 1 +1.13 75.2 2.6 1 +1.42 60.9 2.1 1 +1.79 49.1 1.5 1 +2.25 39.3 1.2 1 +2.84 35.1 1.2 1 +3.57 28.8 1.2 1 +4.50 24.3 1.1 1 +5.66 20.7 0.8 1 +7.13 16.8 0.8 1 +8.97 14.1 0.8 1 +11.29 11.0 0.7 1 +14.22 8.4 0.7 1 +17.90 7.0 0.6 1 +22.54 5.2 0.6 1 +28.37 3.8 0.5 1 +35.72 3.2 0.5 1 +44.96 3.1 0.5 1 +# 27 11.0 11.5 10.0 0.0 1.0 Li 2006 +0.11 894.5 120.4 1 +0.14 670.3 63.8 1 +0.18 578.0 55.9 1 +0.23 328.5 35.4 1 +0.28 351.9 21.0 1 +0.36 280.7 17.3 1 +0.45 219.9 13.6 1 +0.57 166.9 11.0 1 +0.71 138.9 8.1 1 +0.90 116.4 7.1 1 +1.13 91.4 4.0 1 +1.42 77.0 3.7 1 +1.79 65.6 2.9 1 +2.25 56.5 1.9 1 +2.84 46.2 1.7 1 +3.57 38.2 1.4 1 +4.50 32.2 1.2 1 +5.66 26.8 1.1 1 +7.13 22.0 1.0 1 +8.97 16.8 0.8 1 +11.29 12.7 0.9 1 +14.22 9.3 0.8 1 +17.90 6.9 0.7 1 +22.54 4.4 0.7 1 +28.37 3.1 0.6 1 +35.72 2.5 0.6 1 +44.96 2.0 0.5 1 +# 27 8.77 9.27 13.7 0.0 0.73 Guo 2011 +0.0195 1603.22 1379.97 1 +0.0245 216.79 99.23 1 +0.0309 500.77 205.65 1 +0.0389 651.25 227.97 1 +0.0490 414.40 127.83 1 +0.0617 505.70 131.79 1 +0.0776 439.48 95.99 1 +0.0977 419.85 67.85 1 +0.1230 432.36 74.57 1 +0.1549 429.55 60.49 1 +0.1950 341.81 48.61 1 +0.2455 312.98 46.26 1 +0.3090 276.02 40.03 1 +0.3890 271.55 35.56 1 +0.4898 218.92 30.21 1 +0.6166 206.43 29.65 1 +0.7762 158.47 21.87 1 +0.9772 140.44 21.54 1 +1.2303 104.18 15.98 1 +1.5488 83.17 13.21 1 +1.9498 61.00 10.11 1 +2.4547 45.10 7.90 1 +3.0903 34.29 6.46 1 +3.8905 25.31 4.94 1 +4.8978 16.05 3.20 1 +6.1660 14.02 2.88 1 +7.7625 11.85 2.51 1 +# 31 9.27 9.77 13.7 0.0 0.73 Guo 2011 +0.0155 2413.89 1696.21 1 +0.0195 772.35 371.95 1 +0.0245 907.73 320.33 1 +0.0309 999.49 245.60 1 +0.0389 767.58 185.77 1 +0.0490 842.42 160.86 1 +0.0617 757.80 135.63 1 +0.0776 866.28 136.25 1 +0.0977 671.82 81.15 1 +0.1230 668.61 75.96 1 +0.1549 681.90 68.53 1 +0.1950 593.72 53.54 1 +0.2455 526.30 45.11 1 +0.3090 486.74 45.33 1 +0.3890 439.99 39.02 1 +0.4898 353.68 29.26 1 +0.6166 305.45 23.44 1 +0.7762 269.00 22.66 1 +0.9772 219.43 20.11 1 +1.2303 178.12 17.90 1 +1.5488 144.12 14.27 1 +1.9498 107.83 11.15 1 +2.4547 83.08 8.47 1 +3.0903 66.67 8.53 1 +3.8905 54.39 7.03 1 +4.8978 38.37 4.74 1 +6.1660 27.65 3.50 1 +7.7625 20.36 2.83 1 +9.7724 15.23 2.25 1 +12.3027 12.59 2.10 1 +15.4882 10.10 1.53 1 +# 31 9.77 10.27 13.7 0.0 0.73 Guo 2011 +0.0155 1834.52 419.30 1 +0.0195 1761.27 341.35 1 +0.0245 1493.66 233.17 1 +0.0309 1099.02 140.79 1 +0.0389 1171.32 123.02 1 +0.0490 977.27 89.70 1 +0.0617 846.55 72.67 1 +0.0776 714.42 61.32 1 +0.0977 712.13 61.13 1 +0.1230 645.73 58.31 1 +0.1549 613.90 58.17 1 +0.1950 554.84 53.39 1 +0.2455 467.48 40.82 1 +0.3090 390.67 28.84 1 +0.3890 360.66 27.71 1 +0.4898 307.86 23.19 1 +0.6166 264.95 21.95 1 +0.7762 218.54 15.14 1 +0.9772 178.22 14.77 1 +1.2303 137.49 10.36 1 +1.5488 110.30 8.31 1 +1.9498 86.35 6.51 1 +2.4547 71.11 5.79 1 +3.0903 57.80 4.44 1 +3.8905 47.60 3.51 1 +4.8978 39.71 3.29 1 +6.1660 32.60 2.60 1 +7.7625 26.02 1.84 1 +9.7724 19.88 1.38 1 +12.3027 15.79 1.14 1 +15.4882 12.33 0.98 1 +# 33 10.27 10.77 13.7 0.0 0.73 Guo 2011 +0.0155 3503.02 226.60 1 +0.0195 2229.51 151.07 1 +0.0245 2684.99 173.76 1 +0.0309 1643.22 91.23 1 +0.0389 1619.18 99.84 1 +0.0490 1215.06 63.71 1 +0.0617 1177.91 76.23 1 +0.0776 1008.77 68.35 1 +0.0977 858.30 56.85 1 +0.1230 784.61 53.17 1 +0.1549 643.00 42.59 1 +0.1950 544.42 31.06 1 +0.2455 457.95 28.94 1 +0.3090 395.40 22.56 1 +0.3890 339.18 19.35 1 +0.4898 294.29 19.94 1 +0.6166 251.62 14.74 1 +0.7762 210.63 15.87 1 +0.9772 174.59 11.56 1 +1.2303 141.92 9.83 1 +1.5488 114.42 7.58 1 +1.9498 94.23 5.95 1 +2.4547 77.60 4.31 1 +3.0903 64.32 3.17 1 +3.8905 53.49 2.89 1 +4.8978 45.22 2.79 1 +6.1660 37.97 2.05 1 +7.7625 31.68 1.81 1 +9.7724 26.48 1.39 1 +12.3027 21.66 1.27 1 +15.4882 16.91 0.73 1 +19.4984 12.64 0.60 1 +24.5471 9.52 0.48 1 +# 29 10.77 11.27 13.7 0.0 0.73 Guo 2011 +0.0389 3371.49 269.42 1 +0.0490 3092.04 219.00 1 +0.0617 2448.21 206.58 1 +0.0776 1780.64 147.59 1 +0.0977 1454.31 100.79 1 +0.1230 1278.35 90.54 1 +0.1549 1045.78 75.65 1 +0.1950 905.84 68.27 1 +0.2455 755.71 59.23 1 +0.3090 558.69 37.02 1 +0.3890 450.38 31.21 1 +0.4898 386.94 24.46 1 +0.6166 302.39 23.25 1 +0.7762 241.39 16.73 1 +0.9772 194.91 11.42 1 +1.2303 159.45 10.32 1 +1.5488 130.23 9.03 1 +1.9498 105.67 6.68 1 +2.4547 90.05 6.10 1 +3.0903 75.62 5.24 1 +3.8905 64.23 4.55 1 +4.8978 53.76 3.15 1 +6.1660 44.70 2.55 1 +7.7625 36.81 1.93 1 +9.7724 29.62 1.69 1 +12.3027 24.00 1.52 1 +15.4882 18.57 1.29 1 +19.4984 14.09 0.91 1 +24.5471 10.59 0.44 1 +# 17 11.27 11.77 13.7 0.0 0.73 Guo 2011 +0.7762 641.29 62.66 1 +0.9772 524.66 39.53 1 +1.2303 342.71 24.27 1 +1.5488 272.71 12.61 1 +1.9498 216.66 14.68 1 +2.4547 193.26 11.33 1 +3.0903 157.09 9.69 1 +3.8905 128.73 8.92 1 +4.8978 107.40 6.95 1 +6.1660 91.82 4.96 1 +7.7625 69.92 5.27 1 +9.7724 58.43 3.69 1 +12.3027 47.11 3.41 1 +15.4882 36.70 2.21 1 +19.4984 30.17 1.54 1 +24.5471 21.18 0.68 1 +30.9030 15.84 0.90 1 +# 14 9.0 9.5 10.0 0.0 1.0 Yang 2012 +0.12 373.399 200.617 1 +0.19 259.170 92.989 1 +0.30 221.851 69.188 1 +0.48 152.048 46.861 1 +0.75 112.231 32.711 1 +1.20 67.752 12.318 1 +1.90 42.485 6.752 1 +3.01 27.063 2.678 1 +4.77 16.820 1.987 1 +7.55 10.066 1.942 1 +11.97 5.762 0.959 1 +18.97 3.637 1.183 1 +30.07 2.613 0.369 1 +47.66 0.486 0.448 1 +# 14 9.5 10.0 10.0 0.0 1.0 Yang 2012 +0.12 388.815 83.198 1 +0.19 288.384 55.778 1 +0.30 228.047 40.157 1 +0.48 161.172 25.446 1 +0.75 111.791 14.929 1 +1.20 69.407 7.387 1 +1.90 45.751 3.630 1 +3.01 30.453 2.027 1 +4.77 20.490 0.995 1 +7.55 12.326 0.888 1 +11.97 6.884 0.677 1 +18.97 4.021 0.539 1 +30.07 2.850 0.229 1 +47.66 0.532 0.463 1 +# 14 10.0 10.5 10.0 0.0 1.0 Yang 2012 +0.12 393.234 30.913 1 +0.19 287.154 25.668 1 +0.30 223.528 14.039 1 +0.48 164.613 7.819 1 +0.75 113.182 3.683 1 +1.20 73.073 1.938 1 +1.90 49.318 0.960 1 +3.01 34.459 0.776 1 +4.77 24.065 0.617 1 +7.55 15.995 0.983 1 +11.97 9.983 0.895 1 +18.97 5.171 0.673 1 +30.07 3.470 0.229 1 +47.66 1.306 0.922 1 +# 14 10.5 11.0 10.0 0.0 1.0 Yang 2012 +0.12 597.466 90.192 1 +0.19 411.860 24.702 1 +0.30 267.002 23.886 1 +0.48 177.249 10.115 1 +0.75 129.185 5.566 1 +1.20 83.692 3.573 1 +1.90 57.954 2.935 1 +3.01 41.691 1.673 1 +4.77 29.157 1.126 1 +7.55 20.199 1.041 1 +11.97 12.782 0.942 1 +18.97 7.316 0.618 1 +30.07 4.050 0.647 1 +47.66 2.619 0.762 1 +# 14 11.0 11.5 10.0 0.0 1.0 Yang 2012 +0.12 2423.422 1684.429 1 +0.19 1340.480 391.143 1 +0.30 746.369 123.150 1 +0.48 366.864 30.737 1 +0.75 241.362 21.748 1 +1.20 137.289 7.388 1 +1.90 98.301 3.801 1 +3.01 66.697 2.229 1 +4.77 45.976 1.532 1 +7.55 31.164 0.951 1 +11.97 18.996 0.569 1 +18.97 10.485 0.414 1 +30.07 5.545 0.297 1 +47.66 2.473 0.213 1 +END diff --git a/parameterfiles/emerge.param b/parameterfiles/emerge.param new file mode 100644 index 0000000..870b0ef --- /dev/null +++ b/parameterfiles/emerge.param @@ -0,0 +1,111 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% % +% Parameter file for the EMERGE code % +% % +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +% General Setup + +ModelName P100 % Name for the Model that will be used for the output directory +UniversesInParallel 1 % Number of Universes that will be computed in parallel (e.g. for MCMC) +NumFilesInParallel 4 % Number of file read/written in parallel +Verbose 1 % Flag that defines how verbose the screen output is (1: little, 4: all) +MaxMemSize 1500 % Size of maximum memory consumption in MB +BufferSize 100 % Size of communication buffer in MB + + +% Relevant files + +TreefileName trees/P100 % Input tree file +SMFfileName data/smf.dat % Observed stellar mass functions +FQfileName data/fq.dat % Observed quenched fractions +CSFRDfileName data/csfrd.dat % Observed cosmic star formation rate density +SSFRfileName data/ssfr.dat % Observed specific star formation rates +WPfileName data/wp.dat % Observed projected correlation functions + + +%Cosmology + +HubbleParam 0.6781 % Hubble Parameter +Omega0 0.3080 % Matter density +OmegaLambda 0.6920 % Dark Energy density +OmegaBaryon 0.0484 % Baryonic Density + + +% Simulation Parameters + +BoxSize 67.81 % Same units as the tree file. + + +% Units + +UnitLength_in_Mpc 1.0 % Length unit in Mpc +UnitTime_in_yr 1.0e9 % Time unit in yr (use 1e9 for Gyr) +UnitMass_in_Msun 1.0e9 % Mass unit in Msun + + +% SF Efficiency Parameters + +Eff_MassPeak 11.34829 % Halo mass of the peak SF efficiency +Eff_Normalisation 0.009010 % Normalization of the SFE +Eff_LowMassSlope 3.094621 % Low Mass slope of the SFE +Eff_HighMassSlope 1.107304 % High Mass slope of the SFE +Eff_MassPeak_Z 0.654238 % Redshift Evolution of the Halo mass of the peak SF efficiency +Eff_Normalisation_Z 0.596666 % Redshift Evolution of the Normalization of the SFE +Eff_LowMassSlope_Z -2.019841 % Redshift Evolution of the Low Mass slope of the SFE + + +% Satellite galaxy parameters + +Fraction_Escape_ICM 0.562183 % Fraction of the stellar mass of a satellite that goes to the ICM when merging with the central +Fraction_Stripping 0.004015 % Fraction of the peak halo mass that has to be reach to completely strip the stellar mass to the ICM +Timescale_Quenching 4.461039 % Fraction of the dynamical time (at peak mass) when the SFR is quenched +Slope_Quenching 0.346817 % Slope of the power law relation between quenching timescale and stellar mass + + +% Adjustable parameters + +Mstar_min 7.00 % Minimum stellar mass for the stellar mass array used for the statistics (SMFs, FQs, SSFRs) +Mstar_max 12.50 % Maximum stellar mass for the stellar mass array used for the statistics (SMFs, FQs, SSFRs) +Delta_Mstar 0.20 % Bin size for the stellar mass array +Observation_Error_0 0.08 % Scatter between true and observed stellar mass at z=0 +Observation_Error_z 0.06 % Evolution of scatter between true and observed stellar mass +Global_Sigma_SMF_LZ 0.15 % Global error of the SMF at low redshift (in dex) +Global_Sigma_SMF_HZ 0.30 % Global error of the SMF at high redshift (in dex) +Global_Sigma_FQ 0.10 % Global error of the quenched fractions (absolute) +Global_Sigma_CSFRD 0.10 % Global error of the CSFRD (in dex) +Global_Sigma_SSFR 0.15 % Global error of the SSFR (in dex) +Global_Sigma_WP 0.40 % Global error of the WP (in percent) + + +% Fitting parameters + +Eff_MassPeak_Range 0.200 % Range of Halo mass of the peak SF efficiency +Eff_Normalisation_Range 0.500 % Range of log10 Normalization of the SFE +Eff_LowMassSlope_Range 0.500 % Range of Low Mass slope of the SFE +Eff_HighMassSlope_Range 0.200 % Range of High Mass slope of the SFE +Eff_MassPeak_Z_Range 0.200 % Range of Redshift Evolution of the Halo mass of the peak SF efficiency +Eff_Normalisation_Z_Range 0.200 % Range of Redshift Evolution of the Normalization of the SFE +Eff_LowMassSlope_Z_Range 0.500 % Range of Redshift Evolution of the Low Mass slope of the SFE +Fraction_Escape_ICM_Range 0.300 % Range of log10 Fraction of the stellar mass of a satellite that goes to the ICM when merging with the central +Fraction_Stripping_Range 0.300 % Range of log10 Fraction of the peak halo mass that has to be reach to completely strip the stellar mass to the ICM +Timescale_Quenching_Range 0.500 % Range of log10 Fraction of the dynamical time (at peak mass) when the SFR is quenched +Slope_Quenching_Range 0.400 % Range of slope of the power law relation between quenching timescale and stellar mass + + +% MCMC parameters + +NumberOfMCMCWalkers 8 % Number of Walkers for the ensemble MCMC (go large!) +MCMCScaleParameter 2.0 % Scale parameter of the MCMC, can be adjusted to get good acceptance fractions +ChainTemperature 100.0 % Temperature of the MCMC/HYBRID chains (Positive: T decreases with step number, Negative: constant T) +TimeLimit 180.0 % Time limit in seconds - code is stopped before this limit +MCMCseed 12345 % Seed for the random numbers used in the MCMC + + +% Output parameters + +NumOutputFiles 1 % Number of Output files that will be written +OutputFormat 1 % Format of the output files (1: ascii, 2: HDF5) +OutputRedshifts 0.1,0.5,1.0 % String of comma-separated-values (no spaces) that set the redshifts at which the galaxy catalogue will be printed +OutputMassThreshold 7.0 % Sets the minimum mass of galaxies that will be printed (in log10 Msun) diff --git a/src/allocate.c b/src/allocate.c new file mode 100644 index 0000000..16ef0f2 --- /dev/null +++ b/src/allocate.c @@ -0,0 +1,600 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File allocate.c // +// These functions have been adapted from the GADGET code developed by Volker Springel // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file allocate.c +/// \brief Contains functions that allocate the memory using a memory manager +/// +/// This module handles the dynamic memory allocation. +/// To avoid memory allocation/dellocation overhead a big chunk of memory (which will be the +/// maximum amount of dynamically allocatable memory) is allocated upon initialization. This +/// chunk is then filled by the memory blocks as in a stack structure. The blocks are +/// automatically aligned to a 64 bit boundary. Memory blocks come in two flavours: movable and +/// non-movable. In non-movable blocks the starting address is fixed once the block is allocated +/// and cannot be changed. Due to the stack structure of the dynamic memory, this implies that +/// the last (non-movable) block allocated must be the first block to be deallocated. If this +/// condition is not met, an abort condition is triggered. If more flexibility is needed, movable +/// memory blocks can be used. In this case, the starting address of the block is again fixed +/// upon allocation but the block can be shifted (therefore its initial address changes) +/// according to needs. For a movable block to be successfully shifted it is required that all +/// the subsequent allocated blocks are movable. Again, an abort condition is triggered if this +/// condition is not met. Movable blocks can be deallocated in any order provided that the +/// condition just described holds. The gap resulting form the deallocation of a block that is +/// not in the last position will be automatically filled by shifting all the blocks coming after +///the deallocated block. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Manager for dynamic memory allocation developed as part of the GADGET CODE by Volker Springel // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define MAXBLOCKS 5000 ///< Maximum number of blocks that can be allocated +#define MAXCHARS 19 ///< Maximum number of characters a tag can have + +static size_t TotBytes; ///< The total dimension (in bytes) of dynamic memory available to the current task. +static void *Base; ///< Base pointer (initial memory address) of the stack. + +static unsigned long Nblocks; ///< The current number of allocated memory blocks. + +static void **Table; ///< Table containing the initial addresses of the allocated memory blocks. +static size_t *BlockSize; ///< Array containing the size (in bytes) of all the allocated memory blocks. +static char *MovableFlag; ///< Identifies whether a block is movable. +static void ***BasePointers; ///< Base pointers containing the initial addresses of movable memory blocks. +static size_t GlobHighMarkBytes = 0; ///< The maximum number of bytes allocated by all tasks. +static char *VarName; ///< The name of the variable with which the block has been allocated. +static char *FunctionName; ///< The function name that has allocated the memory block. +static char *FileName; ///< The file name where the function that has allocated the block is called. +static int *LineNumber; ///< The line number in FileName where the function that allocated the block has been called. + + +/*! \brief Initialize memory manager. + * + * This function initializes the memory manager. In particular, it sets + * the global variables of the module to their initial value and allocates + * the memory for the stack. + */ +void malloc_init(void) +{ + size_t n; + + BlockSize = (size_t *) malloc(MAXBLOCKS * sizeof(size_t)); + Table = (void **) malloc(MAXBLOCKS * sizeof(void *)); + MovableFlag = (char *) malloc(MAXBLOCKS * sizeof(char)); + BasePointers = (void ***) malloc(MAXBLOCKS * sizeof(void **)); + VarName = (char *) malloc(MAXBLOCKS * MAXCHARS * sizeof(char)); + FunctionName = (char *) malloc(MAXBLOCKS * MAXCHARS * sizeof(char)); + FileName = (char *) malloc(MAXBLOCKS * MAXCHARS * sizeof(char)); + LineNumber = (int *) malloc(MAXBLOCKS * sizeof(int)); + + memset(VarName, 0, MAXBLOCKS * MAXCHARS); + memset(FunctionName, 0, MAXBLOCKS * MAXCHARS); + memset(FileName, 0, MAXBLOCKS * MAXCHARS); + + n = All.MaxMemSize * ((size_t) 1024 * 1024); + + if(n & 63) + endrun("Want 64 byte aligned address"); + + if(!(Base = malloc(n))) + { + printf("Failed to allocate memory for `Base' (%d Mbytes).\n", All.MaxMemSize); + endrun("failure to allocate memory"); + } + + TotBytes = FreeBytes = n; + + AllocatedBytes = 0; + Nblocks = 0; + HighMarkBytes = 0; +} + + +/*! \brief Output memory usage for the task with the greatest amount of memory allocated. + * + * \param OldHighMarkBytes old value of the maximum number of bytes allocated. If the current maximum amount of bytes is less than this value no + output is done + * \param label contains the neme of the code module which requested the memory report (e.g. RUN, ...) + * \param func name of function that has requested the memory usage report (usually given by the __FUNCTION__ macro) + * \param file file where the function that has requested the memory usage report resides (usually given by the __FILE__ macro) + * \param line line number of file where the function that has requested the memory usage was called (usually given by the __LINE__ macro) + */ +void report_detailed_memory_usage_of_largest_task(size_t * OldHighMarkBytes, const char *label, const char *func, const char *file, int line) +{ + size_t *sizelist, maxsize, minsize; + double avgsize; + int i, task; + + sizelist = (size_t *) emalloc("sizelist", NTask * sizeof(size_t)); + MPI_Allgather(&AllocatedBytes, sizeof(size_t), MPI_BYTE, sizelist, sizeof(size_t), MPI_BYTE, MPI_COMM_WORLD); + + for(i = 1, task = 0, maxsize = minsize = sizelist[0], avgsize = sizelist[0]; i < NTask; i++) + { + if(sizelist[i] > maxsize) + { + maxsize = sizelist[i]; + task = i; + } + if(sizelist[i] < minsize) + { + minsize = sizelist[i]; + } + avgsize += sizelist[i]; + } + + efree(sizelist); + + if(maxsize > GlobHighMarkBytes) + GlobHighMarkBytes = maxsize; + + if(maxsize > 1.1 * (*OldHighMarkBytes)) + { + *OldHighMarkBytes = maxsize; + + avgsize /= NTask; + + if(ThisTask == task) + { + char *buf = emalloc("buf", 200 * (Nblocks + 10)); + int cc = 0; + cc += sprintf + (buf + cc, "\n\nAt '%s', %s()/%s/%d: Largest Allocation = %g Mbyte (on task=%d), Smallest = %g Mbyte, Average = %g Mbyte (Past Largest: %g Mbyte)\n\n", + label, func, file, line, maxsize / (1024.0 * 1024.0), task, minsize / (1024.0 * 1024.0), avgsize / (1024.0 * 1024.0), + GlobHighMarkBytes / (1024.0 * 1024.0)); + cc += dump_memory_table_buffer(buf + cc); + if(task == 0) + { + fprintf(FpMemory, "%s", buf); + fflush(FpMemory); + } + else + { + MPI_Send(&cc, 1, MPI_INT, 0, TAG_N, MPI_COMM_WORLD); + MPI_Send(buf, cc + 1, MPI_BYTE, 0, TAG_HDATA, MPI_COMM_WORLD); + } + efree(buf); + } + + if(ThisTask == 0 && task > 0) + { + int cc; + MPI_Recv(&cc, 1, MPI_INT, task, TAG_N, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + char *buf = emalloc("buf", cc + 1); + MPI_Recv(buf, cc + 1, MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD, MPI_STATUS_IGNORE); + fprintf(FpMemory, "%s", buf); + fflush(FpMemory); + efree(buf); + } + + fflush(stdout); + MPI_Barrier(MPI_COMM_WORLD); + } +} + + +/*! \brief Dump the buffer where the memory information is stored to the standard output. + * + */ +void dump_memory_table(void) +{ + char *buf = malloc(200 * (Nblocks+10)); + dump_memory_table_buffer(buf); + printf("%s", buf); + free(buf); +} + + +/*! \brief Fill the output buffer with the memory log. + * + * \param p output buffer + * \return the number of charcter written to p + */ +int dump_memory_table_buffer(char *p) +{ + int i, cc = 0; + size_t totBlocksize = 0; + + cc += sprintf(p + cc, "-------------------------- Allocated Memory Blocks----------------------------------------\n"); + cc += sprintf(p + cc, "Task Nr F Variable MBytes Cumulative Function/File/Linenumber\n"); + cc += sprintf(p + cc, "------------------------------------------------------------------------------------------\n"); + for(i = 0; i < Nblocks; i++) + { + totBlocksize += BlockSize[i]; + + cc += sprintf(p + cc, "%4d %5d %d %19s %10.4f %10.4f %s()/%s/%d\n", + ThisTask, i, MovableFlag[i], VarName + i * MAXCHARS, BlockSize[i] / (1024.0 * 1024.0), + totBlocksize / (1024.0 * 1024.0), FunctionName + i * MAXCHARS, FileName + i * MAXCHARS, LineNumber[i]); + } + cc += sprintf(p + cc, "------------------------------------------------------------------------------------------\n"); + + return cc; +} + + +/*! \brief Allocate a non-movable memory block and store the relative information. + * + * \param varname name of the variable to be stored in the allocated block + * \param n size of the memory block in bytes + * \param func name of function that has called the allocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the allocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the allocation routine was called (usually given by the __LINE__ macro) + * \return a pointer to the beginning of the allocated memory block + */ +void *malloc_fullinfo(const char *varname, size_t n, const char *func, const char *file, int line) +{ + char msg[1000]; + + if((n % 64) > 0) + n = (n / 64 + 1) * 64; + + if(n < 64) + n = 64; + + if(Nblocks >= MAXBLOCKS) + { + sprintf(msg, "Task=%d: No blocks left in malloc_fullinfo() at %s()/%s/line %d. MAXBLOCKS=%d\n", ThisTask, func, file, line, MAXBLOCKS); + endrun(msg); + } + + if(n > FreeBytes) + { + dump_memory_table(); + sprintf + (msg, "\nTask=%d: Not enough memory in malloc_fullinfo() to allocate %g MB for variable '%s' at %s()/%s/line %d (FreeBytes=%g MB).\n", + ThisTask, n / (1024.0 * 1024.0), varname, func, file, line, FreeBytes / (1024.0 * 1024.0)); + endrun(msg); + } + Table[Nblocks] = Base + (TotBytes - FreeBytes); + FreeBytes -= n; + + strncpy(VarName + Nblocks * MAXCHARS, varname, MAXCHARS - 1); + strncpy(FunctionName + Nblocks * MAXCHARS, func, MAXCHARS - 1); + strncpy(FileName + Nblocks * MAXCHARS, file, MAXCHARS - 1); + LineNumber[Nblocks] = line; + + AllocatedBytes += n; + BlockSize[Nblocks] = n; + MovableFlag[Nblocks] = 0; + + Nblocks += 1; + + if(AllocatedBytes > HighMarkBytes) + HighMarkBytes = AllocatedBytes; + + return Table[Nblocks - 1]; +} + + +/*! \brief Allocate a movable memory block and store the relative information. + * + * \param ptr pointer to the initial memory address of the block + * \param varname name of the variable to be stored in the allocated block + * \param n size of the memory block in bytes + * \param func name of function that has called the allocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the allocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the allocation routine was called (usually given by the __LINE__ macro) + * \return a pointer to the beginning of the allocated memory block + */ +void *malloc_movable_fullinfo(void *ptr, const char *varname, size_t n, const char *func, const char *file, int line) +{ + char msg[1000]; + + if((n % 64) > 0) + n = (n / 64 + 1) * 64; + + if(n < 64) + n = 64; + + if(Nblocks >= MAXBLOCKS) + { + sprintf(msg, "Task=%d: No blocks left in malloc_fullinfo() at %s()/%s/line %d. MAXBLOCKS=%d\n", ThisTask, func, file, line, MAXBLOCKS); + endrun(msg); + } + + if(n > FreeBytes) + { + dump_memory_table(); + sprintf + (msg, "\nTask=%d: Not enough memory in malloc_fullinfo() to allocate %g MB for variable '%s' at %s()/%s/line %d (FreeBytes=%g MB).\n", + ThisTask, n / (1024.0 * 1024.0), varname, func, file, line, FreeBytes / (1024.0 * 1024.0)); + endrun(msg); + } + Table[Nblocks] = Base + (TotBytes - FreeBytes); + FreeBytes -= n; + + strncpy(VarName + Nblocks * MAXCHARS, varname, MAXCHARS - 1); + strncpy(FunctionName + Nblocks * MAXCHARS, func, MAXCHARS - 1); + strncpy(FileName + Nblocks * MAXCHARS, file, MAXCHARS - 1); + LineNumber[Nblocks] = line; + + AllocatedBytes += n; + BlockSize[Nblocks] = n; + MovableFlag[Nblocks] = 1; + BasePointers[Nblocks] = ptr; + + Nblocks += 1; + + if(AllocatedBytes > HighMarkBytes) + HighMarkBytes = AllocatedBytes; + + return Table[Nblocks - 1]; +} + + +/*! \brief Deallocate a non-movable memory block. + * + * For this operation to be successful the block that has to be deallocated must be the last allocated one. + * + * \param p pointer to the memory block to be deallocated + * \param func name of function that has called the deallocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the deallocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the deallocation routine was called (usually given by the __LINE__ macro) + */ +void free_fullinfo(void *p, const char *func, const char *file, int line) +{ + char msg[1000]; + + if(Nblocks == 0) + { + sprintf(msg, "no allocated blocks that could be freed"); + endrun(msg); + } + + if(p != Table[Nblocks - 1]) + { + dump_memory_table(); + sprintf(msg, "Task=%d: Wrong call of free() at %s()/%s/line %d: not the last allocated block!\n", ThisTask, func, file, line); + endrun(msg); + } + + Nblocks -= 1; + AllocatedBytes -= BlockSize[Nblocks]; + FreeBytes += BlockSize[Nblocks]; +} + + +/*! \brief Deallocate a movable memory block. + * + * For this operation to be successful all the blocks allocated after the block that has to be freed must be of movable type. + * + * \param p pointer to the memory block to be deallocated + * \param func name of function that has called the deallocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the deallocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the deallocation routine was called (usually given by the __LINE__ macro) + */ +void free_movable_fullinfo(void *p, const char *func, const char *file, int line) +{ + int i; + char msg[1000]; + + if(Nblocks == 0) + { + sprintf(msg, "no allocated blocks that could be freed"); + endrun(msg); + } + + // first, let's find the block + int nr; + + for(nr = Nblocks - 1; nr >= 0; nr--) + if(p == Table[nr]) + break; + + if(nr < 0) + { + dump_memory_table(); + sprintf(msg, "Task=%d: Wrong call of free_movable() from %s()/%s/line %d - this block has not been allocated!\n", ThisTask, func, file, line); + endrun(msg); + } + + if(nr < Nblocks - 1) // the block is not the last allocated block + { + // check that all subsequent blocks are actually movable + for(i = nr + 1; i < Nblocks; i++) + if(MovableFlag[i] == 0) + { + dump_memory_table(); + sprintf + (msg, "Task=%d: Wrong call of free_movable() from %s()/%s/line %d - behind block=%d there are subsequent non-movable allocated blocks\n", + ThisTask, func, file, line, nr); + fflush(stdout); + endrun(msg); + } + } + + AllocatedBytes -= BlockSize[nr]; + FreeBytes += BlockSize[nr]; + + size_t offset = -BlockSize[nr]; + size_t length = 0; + + for(i = nr + 1; i < Nblocks; i++) + length += BlockSize[i]; + + if(nr < Nblocks - 1) + memmove(Table[nr + 1] + offset, Table[nr + 1], length); + + for(i = nr + 1; i < Nblocks; i++) + { + Table[i] += offset; + *BasePointers[i] = *BasePointers[i] + offset; + } + + for(i = nr + 1; i < Nblocks; i++) + { + Table[i - 1] = Table[i]; + BasePointers[i - 1] = BasePointers[i]; + BlockSize[i - 1] = BlockSize[i]; + MovableFlag[i - 1] = MovableFlag[i]; + + strncpy(VarName + (i - 1) * MAXCHARS, VarName + i * MAXCHARS, MAXCHARS - 1); + strncpy(FunctionName + (i - 1) * MAXCHARS, FunctionName + i * MAXCHARS, MAXCHARS - 1); + strncpy(FileName + (i - 1) * MAXCHARS, FileName + i * MAXCHARS, MAXCHARS - 1); + LineNumber[i - 1] = LineNumber[i]; + } + + Nblocks -= 1; +} + + +/*! \brief Reallocate an existing non-movable memory block. + * + * For this operation to be successful this must be the last allocated block. + * + * \param p pointer to the existing memory block to be reallocated + * \param n the new size of the memory block in bytes + * \param func name of function that has called the reallocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the reallocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the reallocation routine was called (usually given by the __LINE__ macro) + * \return a pointer to the beginning of the newly allocated memory block + */ +void *realloc_fullinfo(void *p, size_t n, const char *func, const char *file, int line) +{ + char msg[1000]; + + if((n % 64) > 0) + n = (n / 64 + 1) * 64; + + if(n < 64) + n = 64; + + if(Nblocks == 0) + { + sprintf(msg, "no allocated blocks that could be reallocated"); + endrun(msg); + } + + if(p != Table[Nblocks - 1]) + { + dump_memory_table(); + sprintf(msg, "Task=%d: Wrong call of realloc() at %s()/%s/line %d - not the last allocated block!\n", ThisTask, func, file, line); + endrun(msg); + } + + AllocatedBytes -= BlockSize[Nblocks - 1]; + FreeBytes += BlockSize[Nblocks - 1]; + + if(n > FreeBytes) + { + dump_memory_table(); + sprintf + (msg, "Task=%d: Not enough memory in remalloc(n=%g MB) at %s()/%s/line %d. previous=%g FreeBytes=%g MB\n", + ThisTask, n / (1024.0 * 1024.0), func, file, line, BlockSize[Nblocks - 1] / (1024.0 * 1024.0), FreeBytes / (1024.0 * 1024.0)); + endrun(msg); + } + Table[Nblocks - 1] = Base + (TotBytes - FreeBytes); + FreeBytes -= n; + + AllocatedBytes += n; + BlockSize[Nblocks - 1] = n; + + if(AllocatedBytes > HighMarkBytes) + HighMarkBytes = AllocatedBytes; + + return Table[Nblocks - 1]; +} + + +/*! \brief Reallocate an existing movable memory block. + * + * For this operation to be successful all the blocks allocated after the block that has to be reallocated must be of movable type. + * + * \param p pointer to the existing memory block to be reallocated + * \param n the new size of the memory block in bytes + * \param func name of function that has called the reallocation routine (usually given by the __FUNCTION__ macro) + * \param file file where the function that has called the reallocation routine resides (usually given by the __FILE__ macro) + * \param line line number of file where the reallocation routine was called (usually given by the __LINE__ macro) + * \return a pointer to the beginning of the newly allocated memory block + */ +void *realloc_movable_fullinfo(void *p, size_t n, const char *func, const char *file, int line) +{ + int i; + char msg[1000]; + + if((n % 64) > 0) + n = (n / 64 + 1) * 64; + + if(n < 64) + n = 64; + + if(Nblocks == 0) + { + sprintf(msg, "no allocated blocks that could be reallocated"); + endrun(msg); + } + + // first, let's find the block + int nr; + + for(nr = Nblocks - 1; nr >= 0; nr--) + if(p == Table[nr]) + break; + + if(nr < 0) + { + dump_memory_table(); + sprintf(msg, "Task=%d: Wrong call of realloc_movable() from %s()/%s/line %d - this block has not been allocated!\n", ThisTask, func, file, line); + endrun(msg); + } + + if(nr < Nblocks - 1) // the block is not the last allocated block + { + // check that all subsequent blocks are actually movable + for(i = nr + 1; i < Nblocks; i++) + if(MovableFlag[i] == 0) + { + dump_memory_table(); + sprintf + (msg, "Task=%d: Wrong call of realloc_movable() from %s()/%s/line %d - behind block=%d there are subsequent non-movable allocated blocks\n", + ThisTask, func, file, line, nr); + endrun(msg) + } + } + + AllocatedBytes -= BlockSize[nr]; + FreeBytes += BlockSize[nr]; + + if(n > FreeBytes) + { + dump_memory_table(); + sprintf + (msg, "Task=%d: at %s()/%s/line %d: Not enough memory in remalloc_movable(n=%g MB). previous=%g FreeBytes=%g MB\n", + ThisTask, func, file, line, n / (1024.0 * 1024.0), BlockSize[nr] / (1024.0 * 1024.0), FreeBytes / (1024.0 * 1024.0)); + endrun(msg); + } + + size_t offset = n - BlockSize[nr]; + size_t length = 0; + + for(i = nr + 1; i < Nblocks; i++) + length += BlockSize[i]; + + if(nr < Nblocks - 1) + memmove(Table[nr + 1] + offset, Table[nr + 1], length); + + for(i = nr + 1; i < Nblocks; i++) + { + Table[i] += offset; + + *BasePointers[i] = *BasePointers[i] + offset; + } + + FreeBytes -= n; + AllocatedBytes += n; + BlockSize[nr] = n; + + if(AllocatedBytes > HighMarkBytes) + HighMarkBytes = AllocatedBytes; + + return Table[nr]; +} diff --git a/src/allvars.c b/src/allvars.c new file mode 100644 index 0000000..f37eb12 --- /dev/null +++ b/src/allvars.c @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File allvars.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file allvars.c +/// \brief Contains global variables and declarations +/// +/// This file declares all global variables. Further variables should be added here and in the +/// file allvarsh.h where they should be declared as 'extern'. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include "allvars.h" + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Structures // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +struct global_data All; ///< Stores all global variables that are the same on each task +struct halo *H; ///< Stores all haloes and their properties on a given task +struct haloread *HR; ///< Stores all haloes and properties during reading +struct parameters P; ///< Stores all parameters on a given task (all the same within a universe) + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Global variables // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +int Ntrees; // Number of trees on this task +int Nhalos; // Number of haloes on this task +int Nforests; // Number of forests on this task +int *NhalosInTree; // Number of haloes in each tree on this task +int *NhalosInForest; // Number of haloes in each forest on this task +int *NtreesInForest; // Number of trees in each forest on this task +int *OffsetHalos; // Offset of the first halo in a tree/forest +int Norphans; // Number of orphans on this task +IDType *TreeIDInTree; // ID of each tree on this task + +int NThread; // OMP number of threads +int NTask; // MPI number of tasks +int ThisTask; // MPI number of this tasks +int MasterTask; // MPI number of master tasks for each universe + +float *ScaleFactor; // List of all scale factors in the merger trees +float *CosmicTime; // Cosmic time corresponding to the above scale factors +float *Timestep; // Timesteps corresponding the above scale factors +float *DynTime; // Dynamical halo times corresponding the above scale factors + +float *MassLeft; // Stores the fraction of the mass left between each scale factor +float *MassFormed; // Stores the mass of all populations ever formed in a branch +float *ICMFormed; // Stores the mass of all populations in the ICM + +int *Output_iscale; // Simulation snapshot numbers for the output catalogue +float *OutputRedshifts; // Output redshifts of the galaxy catalogue + +//Observational data +struct galaxy_data *Smf; // Stellar Mass Functions +struct galaxy_data *Fq; // Quenched Fractions +struct galaxy_data *Csfrd; // Cosmic star formation rate density +struct galaxy_data *Ssfr; // Specific star formation rates +struct galaxy_data *Wp; // Projected correlation functions +struct data_set *SmfSet; // Information about each data set of the Stellar Mass Functions +struct data_set *FqSet; // Information about each data set of the Quenched Fractions +struct data_set *CsfrdSet; // Information about each data set of the Cosmic star formation rate density +struct data_set *SsfrSet; // Information about each data set of the Specific star formation rates +struct data_set *WpSet; // Information about each bin of the projected correlation function + +//Model statistics +float *Mstar; // Array of stellar masses used for model statistics +float *Modelsmf; // Model stellar mass function (NTimestep * Nmstar); +float *Modelsmfred; // Model stellar mass function for red galaxies (NTimestep * Nmstar); +float *Modelcsfrd; // Model cosmic SFR density (NTimestep) +float *Modelssfr; // Model specific SFRs (NTimestep * Nmstar) +float *Radius; // Array of radii used for the correlation function +float *Modelxi; // Model 3d auto-correlation function +float *Modelxierr; // Model 3d auto-correlation function error +struct kd_pos *Galpos; // Positions of all galaxies in a stellar mass bin of the correlation function + +//Fitting +int DoF; // Total degrees of freedom +int DoFsmf; // Degrees of freedom for the SMF +int DoFfq; // Degrees of freedom for the FQ +int DoFcsfrd; // Degrees of freedom for the CSFRD +int DoFssfr; // Degrees of freedom for the SSFR +int DoFwp; // Degrees of freedom for the WP + +//Memory +int EndFlag; // Flag for the endrun Macro +size_t AllocatedBytes; // Number of allocated Bytes +size_t HighMarkBytes; // Highest mark of allocated Bytes +size_t FreeBytes; // Number of free Bytes +size_t HighMark; // High Mark + +gsl_rng *rng_gaussian; // Gaussian random number generator +gsl_rng *rng_uniform; // Uniform random number generator + +void *CommBuffer; // Points to communication buffer, which is used at a few places */ + +FILE *FpInfo, // File pointer for info.txt log-file. + *FpMemory; // File pointer for memory.txt log-file. + +#if (RANDOM_NUMBER_TABLE > 0) +float *RndTableGaussian; // Random number table filled with gaussian random numbers +float *RndTableUniform; // Random number table filled with uniform random numbers +#endif + +#ifdef WRITE_MAINBRANCH +float *MainBranchMasses; // Masses for the main branch history outputs +float *MainBranchBinSize; // Mass bin sizes for the main branch history outputs +#endif diff --git a/src/allvars.h b/src/allvars.h new file mode 100644 index 0000000..bede944 --- /dev/null +++ b/src/allvars.h @@ -0,0 +1,639 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File allvars.h // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file allvars.h +/// \brief Contains global variables and declarations +/// +/// This file declares all global variables. Further variables should be added here, and declared +/// as 'extern'. The actual existence of these variables is provided by the file 'allvars.c'. +/// To produce 'allvars.c' from 'allvars.h', do the following: +/// +/// - Erase all #define statements +/// - add #include "allvars.h" +/// - delete all keywords 'extern' +/// - delete all struct definitions enclosed in {...}, +/// e.g. "extern struct global_data {....} All;" +/// becomes "struct global_data All;" +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef ALLVARS_H +#define ALLVARS_H + +#include +#include +#include +#include +#include +#include + +#ifdef _OPENMP +#include +#endif + +#include "codeoptions.h" + +#define CODE "EMERGE" ///< Code name +#define BRANCH "master" ///< Branch name +#define VERSION "1.0.0" ///< Code version +#define SYMBOL '#' ///< Symbol used to start output lines + +#define NSTRING 200 ///< Number of elements used for a generic string +#define SSTRING 50 ///< Number of elements used for a short string +#define LINESIZE 100 ///< Number of characters used for the screen output +#define ALLOC_TOLERANCE 0.2 ///< Tolerance in the memory allocation + +#define ZMAX_SMFERR 4.0 ///< Maximum redshift up to which the observed stellar mass error increases +#define SSFRTHRESH 0.3 ///< Fraction of the inverse Hubble time used for the SSFR threshold +#define SIGMASMFZTHRESH 0.1 ///< Redshift that divides the SMF for the global sigma assignment + +#define MAXTIMESTEPS 10000 ///< Maximum number of timesteps for the simulation +#define NRNDTABLEMIN 1000 ///< Minimum number of entries for the random number table +#define NALLOCATION 1000 ///< Number of elements that are used for dynamic allocation + +#define TREEDIM 3 ///< Number of dimensions of the kd-tree used for the clustering + +#if !defined(OPENMPTHREADS) +#define OPENMPTHREADS 1 ///< Default number of OpenMP threads (1: OpenMP disabled) +#endif + +#if !defined(RANDOM_NUMBER_TABLE) +#define RANDOM_NUMBER_TABLE 0 ///< Default number entries for the random number tables (0: disabled) +#endif + +#if !defined(WP_RBINS) +#define WP_RBINS 20 ///< Number of radial bins for the 3D correlation functions +#endif + +#if !defined(WP_RBINS_INT) +#define WP_RBINS_INT 1000 ///< Number of radial bins for the interpolation of the 3D correlation function +#endif + +#if !defined(WP_RMAX) +#define WP_RMAX 0.1 ///< Maximum radius (given as a fraction of the box size) of the 3D correlation functions +#endif + +#if !defined(WP_NLEAF_MIN) +#define WP_NLEAF_MIN 4 ///< Minimum number of galaxies to split a node of the kd-tree +#endif + +#if !defined(WP_NODE_WIDTH_MIN) +#define WP_NODE_WIDTH_MIN 0.01 ///< Minimum width of each node in units of the box size +#endif + +#ifndef LONGIDS +typedef unsigned int IDType; ///< Integer type used for IDs +#else +typedef unsigned long long IDType; ///< Integer type used for IDs if LONGIDS is selected +#endif + +// +// Macro functions \cond +#define endrun(...) {if(EndFlag==0) {char termbuf1[1000], termbuf2[1000]; sprintf(termbuf1, "%s Code was stopped on task=%d, function %s(), file %s, line %d", All.startline, ThisTask, __FUNCTION__, __FILE__, __LINE__); sprintf(termbuf2, __VA_ARGS__); printf("%s: %s\n", termbuf1, termbuf2); fflush(stdout); EndFlag=1; MPI_Abort(MPI_COMM_WORLD, 1);} exit(0);} + +//Minimum and maximum of two values +#define min(X, Y) ((X) < (Y) ? (X) : (Y)) +#define max(X, Y) ((X) > (Y) ? (X) : (Y)) + +//Square and cube of a value +#define sqr(X) ((X)*(X)) +#define cub(X) ((X)*(X)*(X)) + +//Nearest distance to a different position given periodic boundary conditions +#define NEAREST(x) (((x)>0.5*All.Lbox) ? ((x)-All.Lbox) : (((x)<-0.5*All.Lbox) ? ((x)+All.Lbox) : (x))) + +//Memory manager Macro functions +#ifndef DISABLE_MEMORY_MANAGER +#define emalloc(x, y) malloc_fullinfo(x, y, __FUNCTION__, __FILE__, __LINE__) +#define emalloc_movable(x, y, z) malloc_movable_fullinfo(x, y, z, __FUNCTION__, __FILE__, __LINE__) + +#define erealloc(x, y) realloc_fullinfo(x, y, __FUNCTION__, __FILE__, __LINE__) +#define erealloc_movable(x, y) realloc_movable_fullinfo(x, y, __FUNCTION__, __FILE__, __LINE__) + +#define efree(x) free_fullinfo(x, __FUNCTION__, __FILE__, __LINE__) +#define efree_movable(x) free_movable_fullinfo(x, __FUNCTION__, __FILE__, __LINE__) + +#define report_memory_usage(x, y) report_detailed_memory_usage_of_largest_task(x, y, __FUNCTION__, __FILE__, __LINE__) + +//Standard memory allocation if the Memory manager is disabled +#else +#define emalloc(x, y) malloc(y) +#define emalloc_movable(x, y, z) malloc(z) + +#define erealloc(x, y) realloc(x, y) +#define erealloc_movable(x, y) realloc(x, y) + +#define efree(x) free(x) +#define efree_movable(x) free(x) + +#define report_memory_usage(x, y) printf("Memory manager disabled.\n") +#endif +// \endcond + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Structures // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Structure containing the same data for all tasks +/// +/// This structure contains data which is the SAME for all tasks (mostly code parameters read from +/// the parameter file). Holding this data in a structure is convenient for writing/reading the +/// restart file, and it allows the introduction of new global variables in a simple way. The only +/// thing to do is to introduce them into this structure. +/////////////////////////////////////////////////////////////////////////////////////////////////// +extern struct global_data +{ + char treefile_name[NSTRING]; ///< Base name of the tree file + char model_name[NSTRING]; ///< Name of the model + char OutputDir[NSTRING]; ///< Output directory (will be generated if not existent) + char output_redshifts[NSTRING]; ///< Comma separated string of output redshifts + char output_mass_mb[NSTRING]; ///< Comma separated string of masses for main branch output + char output_mass_mb_bin[NSTRING]; ///< Comma separated string of mass bin sizes for main branch output + char fullline[NSTRING]; ///< String with #LINESIZE elements given by #SYMBOL + char startline[NSTRING]; ///< String with 3 elements given by #SYMBOL + + int Mode; ///< Model mode stating what will be done (MCMC, etc) + int verbose; ///< States how verbose the output is + int NUniverses; ///< Number of universes that will be computed in parallel + int NTaskPerUniverse; ///< Number of tasks per universe + int NumOutputFiles; ///< Number of output files for the galaxy catalogue + int NumFilesInParallel; ///< Number of files read/written in parallel + int OutputFormat; ///< Determines which file format is used (1: ascii, 2:hdf5) + int MaxTrees; ///< Maximum number of trees that can be held on one task + int MaxHalos; ///< Maximum number of haloes that can be held on one task + int MaxOrphans; ///< Maximum number of orphans that can be held on one task + int MaxMemSize; ///< Maximum memory size allocated at the start in MB + int BufferSize; ///< Size of the Communication Buffer in MB + int NTimesteps; ///< Number of timesteps in the simulation + int Nparam; ///< Number of free parameters that will be fitted + int Nobs; ///< Number of observed statistics that will be read + int MaxNLeaves; ///< Maximum number of leaves per tree + int NMassFormed; ///< Number of entries per thread for MassFormed (MaxNLeaves*NTimesteps) + int NStatistics; ///< Number of entries per thread for Statistics (Nmstar*NTimesteps) + int Seed; ///< Initial seed for the random number generator + int Nwalkers; ///< Number of walkers for probing parameter space + int Noutputredshifts; ///< Number of redshifts for the output catalogues + int Noutputbranch; ///< Number of mass bins for main branch history output + int MainBranchMassType; ///< Mass type for main branch output (0: halo, 1: stellar) + int MainBranch_iscale; ///< Simulation snapshot numbers for main branch selection redshift + + unsigned long long TotNtrees; ///< Total number of trees + unsigned long long TotNhalos; ///< Total number of haloes + + double h_100; ///< Hubble parameter in units of 100km/s/Mpc + double Omega_0; ///< Matter density at redshift 0 + double Omega_Lambda_0; ///< Dark energy density at redshift 0 + double Omega_Baryon_0; ///< Baryon density at redshift 0 + double f_baryon; ///< Universal baryon fraction + double Hubbletime; ///< Hubble time in internal time units + double Lbox; ///< Side length of the simulation box in internal length units + double obssigma0; ///< Normalisation at redshift 0 of the observational mass uncertainty + double obssigmaz; ///< Redshift slope of the observational mass uncertainty + double mcmca; ///< Scale parameter for the MCMC + double temperature; ///< Starting temperature for the chains + double timelimit; ///< Time limit in seconds after which the run is stopped + double timenow; ///< Current time + double starttime; ///< Time at the start of the run + double minmass; ///< Minimum stellar mass of the galaxy catalogue + double mainBranchRedshift; ///< Redshift at which masses are selected for main branch output + + // Model parameters read from the parameter file + double M0; ///< Characteristic mass of instantaneous conversion efficiency + double Epsilon0; ///< Normalisation of instantaneous conversion efficiency + double Beta0; ///< Low mass slope of instantaneous conversion efficiency + double Gamma0; ///< High mass slope of instantaneous conversion efficiency + double MZ; ///< Evolution of characteristic mass of instantaneous conversion efficiency + double EpsilonZ; ///< Evolution of normalisation of instantaneous conversion efficiency + double BetaZ; ///< Evolution of low mass slope of instantaneous conversion efficiency + double GammaZ; ///< Evolution of high mass slope of instantaneous conversion efficiency + double Fesc; ///< Fracion of stellar mass going to ICM in merger + double Fstrip; ///< Fracion of peak halo mass for which stars get stripped + double Tau0; ///< Fracion of dynamical time after peak halo mass when galaxy is quenched + double TauS; ///< Slope for quenching as function of stellar mass + double TauD; ///< Decay timescale for quenching as fracion of dynamical time + + // Range of model parameters read from the parameter file (prior) + double DeltaM0; ///< Characteristic mass of instantaneous conversion efficiency (Range) + double DeltaEpsilon0; ///< Normalisation of instantaneous conversion efficiency (Range) + double DeltaBeta0; ///< Low mass slope of instantaneous conversion efficiency (Range) + double DeltaGamma0; ///< High mass slope of instantaneous conversion efficiency (Range) + double DeltaMZ; ///< Evolution of characteristic mass of instantaneous conversion efficiency (Range) + double DeltaEpsilonZ; ///< Evolution of normalisation of instantaneous conversion efficiency (Range) + double DeltaBetaZ; ///< Evolution of low mass slope of instantaneous conversion efficiency (Range) + double DeltaGammaZ; ///< Evolution of high mass slope of instantaneous conversion efficiency (Range) + double DeltaFesc; ///< Fracion of stellar mass going to ICM in merger (Range) + double DeltaFstrip; ///< Fracion of peak halo mass for which stars get stripped (Range) + double DeltaTau0; ///< Fracion of dynamical time after peak halo mass when galaxy is quenched (Range) + double DeltaTauS; ///< Slope for quenching as function of stellar mass (Range) + double DeltaTauD; ///< Decay timescale for quenching as fracion of dynamical time (Range) + + double x_unit; ///< Internal length unit + double t_unit; ///< Internal time unit + double m_unit; ///< Internal mass unit + + //Data + char smffile_name[NSTRING]; ///< Name of the file containing the stellar mass functions + char fqfile_name[NSTRING]; ///< Name of the file containing the quenched fractions + char csfrdfile_name[NSTRING]; ///< Name of the file containing the cosmic star formation rate density + char ssfrfile_name[NSTRING]; ///< Name of the file containing the specific star formation rates + char wpfile_name[NSTRING]; ///< Name of the file containing the projected correlation functions + int Nsmf; ///< Number of data points for the stellar mass functions + int Nfq; ///< Number of data points for the quenched fractions + int Ncsfrd; ///< Number of data points for the cosmic star formation rate density + int Nssfr; ///< Number of data points for the specific star formation rates + int Nwp; ///< Number of data points for the projected correlation functions + int Nsmfset; ///< Number of data sets for the stellar mass functions + int Nfqset; ///< Number of data sets for the quenched fractions + int Ncsfrdset; ///< Number of data sets for the cosmic star formation rate density + int Nssfrset; ///< Number of data sets for the specific star formation rates + int Nwpset; ///< Number of mass bins for the projected correlation functions + int Wpscale; ///< Simulation snapshot number for the projected correlation functions + double wpredshift; ///< Global redshift of the projected correlation functions + double wpmmin; ///< Minimum stellar mass for the projected correlation functions + double wpmmax; ///< Maximum stellar mass for the projected correlation functions + double GlobalSigmaSmfLz; ///< Global error for the low redshift stellar mass functions + double GlobalSigmaSmfHz; ///< Global error for the high redshift stellar mass functions + double GlobalSigmaFq; ///< Global error for the quenched fractions + double GlobalSigmaCsfrd; ///< Global error for the cosmic star formation rate density + double GlobalSigmaSsfr; ///< Global error for the specific star formation rates + double GlobalSigmaWp; ///< Global error for the projected correlation functions + + //Statistics + int Nmstar; ///< Number of stellar mass bins for the interpolation + double mstarmin; ///< Minimum stellar mass for the interpolation + double mstarmax; ///< Maximum stellar mass for the interpolation + double dmstar; ///< Stellar mass bin width for the interpolation + +} All; ///< Stores all global variables that are the same on each task + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Halo structure that holds all information on a halo +/// +/// This structure contains all information for each dark matter halo and the galaxy at its centre. +/// The array H is allocated on each task so that it containts all haloes on a given task. +/////////////////////////////////////////////////////////////////////////////////////////////////// +extern struct halo +{ + IDType haloid; ///< ID of the halo + IDType descid; ///< ID of the halo's descendant + IDType upid; ///< ID of the most massive host halo (upid) + + int iscale; ///< Index of the scale factor array corresponding to this halo + int imain; ///< Index of the main co-progenitor + int idesc; ///< Index of the descandant + int iprog; ///< Index of the main progenitor + int impeak; ///< Index of the halo for which the mass reached its peak + + //Galaxy + int icoprog; ///< Index of the next co-progenitor + int idescgal; ///< Index of the descandant + int imass; ///< Index of the 0th element of the mass array MassFormed + int itdf; ///< Index of when dynamical friction time was last computed + + unsigned short np; ///< Number of progenitors + unsigned short mmp; ///< Flag for most massive progenitor (1: yes, 0: no) + unsigned short type; ///< Halo type (0: main halo, 1: subhalo, 2: orphan) + unsigned short gone; ///< Flag indicating if this halo is present or gone + + float pos[3]; ///< 3D position + float vel[3]; ///< 3D velocity + float a; ///< Scale factor + float mvir; ///< Virial mass + float rvir; ///< Virial radius + float c; ///< Concentration \f$(r_\mathrm{vir}/r_\mathrm{s})\f$ + float lambda; ///< Spin parameter + float mdotbary; ///< Baryonic accretion rate averaged over one dynamical time + + //Galaxy + float sfr; ///< Star formation rate + float mstar; ///< Stellar mass + float icm; ///< Intra cluster mass + float tdf; ///< Dynamical friction time + +#ifdef COMPUTE_ICM + IDType forestid; ///< ID of the forest + int ihost; ///< Index of the host halo (possibly on another task) +#endif + +} *H; ///< Stores all haloes and their properties on a given task + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Halo structure that is is used to read in the merger trees in binary form +/// +/// For any merger tree to read the haloes MUST be provided in this structure. Merger trees +/// created with the consistent-trees code can be converted to this format with the +/// convert_CT_to_emerge.c code in the tools/ folder. +/////////////////////////////////////////////////////////////////////////////////////////////////// +extern struct haloread +{ + IDType haloid; ///< ID of the halo + IDType descid; ///< ID of the halo's descendant + IDType upid; ///< ID of the most massive host halo (upid) + + unsigned short np; ///< Number of progenitors + unsigned short mmp; ///< Flag for most massive progenitor (1: yes, 0: no) + + float a; ///< Scale factor + float mvir; ///< Virial mass + float rvir; ///< Virial radius + float c; ///< Concentration (rvir/rs) + float lambda; ///< Spin parameter + + float pos[3]; ///< 3D position + float vel[3]; ///< 3D velocity + +} *HR; ///< Stores all haloes and properties during reading + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Parameter structure that holds all parameters +/// +/// On each task all current parameter values are stored in the structure P. They can be modified +/// during a chain loop but need to the same for each universe. +/////////////////////////////////////////////////////////////////////////////////////////////////// +extern struct parameters +{ + double M0; ///< Characteristic mass of instantaneous conversion efficiency + double Epsilon0; ///< Normalisation of instantaneous conversion efficiency + double Beta0; ///< Low mass slope of instantaneous conversion efficiency + double Gamma0; ///< High mass slope of instantaneous conversion efficiency + double MZ; ///< Evolution of characteristic mass of instantaneous conversion efficiency + double EpsilonZ; ///< Evolution of normalisation of instantaneous conversion efficiency + double BetaZ; ///< Evolution of low mass slope of instantaneous conversion efficiency + double GammaZ; ///< Evolution of high mass slope of instantaneous conversion efficiency + double Fesc; ///< Fracion of stellar mass going to ICM in merger + double Fstrip; ///< Fracion of peak halo mass for which stars get stripped + double Tau0; ///< Fracion of dynamical time after peak halo mass when galaxy is quenched + double TauS; ///< Slope for quenching as function of stellar mass + double TauD; ///< Decay timescale for quenching as fracion of dynamical time + +} P; ///< Stores all parameters on a given task (all the same within a universe) + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Galaxy data structure that holds all information for each observational data point +/// +/// For each observation the structure member can have a different meaning. For the stellar mass +/// function and the quenched fractions obs_x is the stellar mass and obs_bin is the scale factor. +/// For the specific star formation rate obs_x is the scale factor and obs_bin is the stellar mass. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct galaxy_data +{ + int nfit; ///< Weighting in the fit. Should only be different from 1 for testing. + float obs_x; ///< X-value of the observation (SMF/FQ: mstar, CSFRD/SSFR: a) + float obs_y; ///< Y-value of the observation + float obs_sigma; ///< Uncertainty of the observation + float bin; ///< Bin for which the observation has been measured (SMF/FQ: a, CSFRD/SSFR: mstar) + float mod_y; ///< Y-value predicted by the model + float mod_sigma; ///< Uncertainty predicted by the model +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Data set structure that holds all information for each specific data set +/// +/// A structure member contains all information for any given data set. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct data_set +{ + float min; ///< Minimum value of each bin (SMF, FQ: redshift; SSFR, WP: stellar mass) + float max; ///< Maximum value of each bin (SMF, FQ: redshift; SSFR, WP: stellar mass) + float cut; ///< WP: Maximum radius along the line of sight for the projection \f$\pi_\mathrm{max}\f$ + int ndata; ///< Number of data points in each data set + int offset; ///< Offset for each data set (points to the first data point for each data set) + char tag[SSTRING]; ///< Name tag to identify the data set +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Simple structure to create a searchable ID list +/// +/// Each element has an ID and an original position. The list can then be sorted according to the +/// ID and the original position be retrieved by the function binary_search_id. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct search_list_IDs +{ + IDType ID; ///< ID of the element + int position; ///< Original position of the element +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Simple structure to sort the load +/// +/// Each element stores the number of haloes and the load on its task as well as the task number. +/// The list can then be sorted according to the load and the task number be retrieved with +/// TaskSorted. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct task_list +{ + int NhalosInTask; ///< Number of haloes on this task + double LoadInTask; ///< Load in this task + int TaskSorted; ///< Task number +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief kd-tree structure that is used to search for pairs to compute the correlation function +/// +/// A kd-tree consists of node_count nodes, the dimension, the number of objects it contains. +/// Additionally the starting level containing nstartnodes nodes is included. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct kd_tree +{ + struct kd_node *nodes; ///< Array that stores all nodes of the tree + int node_count; ///< Number of nodes in the tree + int count; ///< Number of objects in the tree + int dim; ///< Dimension of the tree + int lmin; ///< Minimum level for the parallel computation of pairs + int lmax; ///< Maximum level for the parallel computation of pairs + int midlevel; ///< Level of the tree for which all nodes will be computed in parallel + int nstartnodes; ///< Number of nodes that will be computed in parallel +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Node structure for the kd-tree that stores the children nodes the count and the bounding box +/// +/// A kd-tree node can be either a leaf (leaf = 1) or a parent node (leaf = 0). If it is a leaf the +/// integers i0 and i1 store the indices of the first and the last object in the leaf. If it is a +/// parent node the integers i0 and i1 store the indices of the children nodes. The number of objects +/// in the node is stored in cnt. The bounding box that is defined by the minimum volume hyper-rectangle +/// around all objects in the node is spanned by the two positions min and max. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct kd_node +{ + int i0; ///< Index pointing to the left child node (if not leaf) or to the first object in the node (if leaf). + int i1; ///< Index pointing to the right child node (if not leaf) or to the last object in the node (if leaf). + int leaf; ///< Flag that defines if the node is a leaf (1) or a parent node (0). + int cnt; ///< Number of objects in the node. + float min[TREEDIM]; ///< Minimum position of the bounding box in all dimensions. + float max[TREEDIM]; ///< Maximum position of the bounding box in all dimensions. +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Position structure for the kd-tree that stores the position of all objects in the tree +/// +/// Each object has a position defined by a #TREEDIM-dimensional array x. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct kd_pos +{ + float x[TREEDIM]; ///< Position array with dimension #TREEDIM. +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Galaxy structure holding all galaxies that are used in the correlation functions +/// +/// Each object has a position defined by a #TREEDIM-dimensional array x, a mass and a sfr. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct galaxy +{ + float x[TREEDIM]; ///< Position array with dimension #TREEDIM. + float mass; ///< Stellar mass of the galaxy + float sfr; ///< Star formation rate of the galaxy +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// \brief Helper structure to build the kd-tree +/// +/// This structure is used to build the kd-tree and stores the index of the node and all needed +/// information about the objects that are contained in the current node. +/////////////////////////////////////////////////////////////////////////////////////////////////// +struct kd_build_task +{ + int node; ///< Index of the current node. + int first; ///< Index of the first object in the current node. + int last; ///< Index of the last object in the current node. +}; + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Global variables // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +extern int Ntrees; ///< Number of trees on this task +extern int Nhalos; ///< Number of haloes on this task +extern int Nforests; ///< Number of forests on this task +extern int *NhalosInTree; ///< Number of haloes in each tree on this task +extern int *NhalosInForest; ///< Number of haloes in each forest on this task +extern int *NtreesInForest; ///< Number of trees in each forest on this task +extern int *OffsetHalos; ///< Offset of the first halo in a tree/forest +extern int Norphans; ///< Number of orphans on this task +extern IDType *TreeIDInTree; ///< ID of each tree on this task + +extern int NThread; ///< OMP number of threads +extern int NTask; ///< MPI number of tasks +extern int ThisTask; ///< MPI number of this tasks +extern int MasterTask; ///< MPI number of master tasks for each universe + +extern int EndFlag; ///< Flag for the endrun Macro + +extern float *ScaleFactor; ///< List of all scale factors in the merger trees +extern float *CosmicTime; ///< Cosmic time corresponding to the above scale factors +extern float *Timestep; ///< Timesteps corresponding the above scale factors +extern float *DynTime; ///< Dynamical halo times corresponding the above scale factors + +extern float *MassLeft; ///< Stores the fraction of the mass left between each scale factor +extern float *MassFormed; ///< Stores the mass of all populations ever formed in a branch +extern float *ICMFormed; ///< Stores the mass of all populations in the ICM + +extern int *Output_iscale; ///< Simulation snapshot numbers for the output catalogue +extern float *OutputRedshifts; ///< Output redshifts of the galaxy catalogue + +//Observational data +extern struct galaxy_data *Smf; ///< Stellar Mass Functions +extern struct galaxy_data *Fq; ///< Quenched Fractions +extern struct galaxy_data *Csfrd; ///< Cosmic star formation rate density +extern struct galaxy_data *Ssfr; ///< Specific star formation rates +extern struct galaxy_data *Wp; ///< Projected correlation functions +extern struct data_set *SmfSet; ///< Information about each data set of the Stellar Mass Functions +extern struct data_set *FqSet; ///< Information about each data set of the Quenched Fractions +extern struct data_set *CsfrdSet; ///< Information about each data set of the Cosmic star formation rate density +extern struct data_set *SsfrSet; ///< Information about each data set of the Specific star formation rates +extern struct data_set *WpSet; ///< Information about each data set of the projected correlation function + +//Model statistics +extern float *Mstar; ///< Array of stellar masses used for model statistics +extern float *Modelsmf; ///< Model stellar mass function (NTimestep * Nmstar) +extern float *Modelsmfred; ///< Model stellar mass function for red galaxies (NTimestep * Nmstar) +extern float *Modelcsfrd; ///< Model cosmic SFR density (NTimestep) +extern float *Modelssfr; ///< Model specific SFRs (NTimestep * Nmstar) +extern float *Radius; ///< Array of radii used for the correlation function +extern float *Modelxi; ///< Model 3d auto-correlation function +extern float *Modelxierr; ///< Model 3d auto-correlation function error +extern struct kd_pos *Galpos; ///< Positions of all galaxies in a stellar mass bin of the correlation function + +//Fitting +extern int DoF; ///< Total degrees of freedom +extern int DoFsmf; ///< Degrees of freedom for the SMF +extern int DoFfq; ///< Degrees of freedom for the FQ +extern int DoFcsfrd; ///< Degrees of freedom for the CSFRD +extern int DoFssfr; ///< Degrees of freedom for the SSFR +extern int DoFwp; ///< Degrees of freedom for the WP + +//Memory +extern size_t AllocatedBytes; ///< Number of allocated Bytes +extern size_t HighMarkBytes; ///< Highest mark of allocated Bytes +extern size_t FreeBytes; ///< Number of free Bytes +extern size_t HighMark; ///< High Mark + +extern gsl_rng *rng_gaussian; ///< Gaussian random number generator +extern gsl_rng *rng_uniform; ///< Uniform random number generator + +extern void *CommBuffer; ///< Points to communication buffer, which is used at a few places + +extern FILE *FpInfo, ///< File pointer for info.txt log-file. + *FpMemory; ///< File pointer for memory.txt log-file. + +#if (RANDOM_NUMBER_TABLE > 0) +extern float *RndTableGaussian; ///< Random number table filled with gaussian random numbers +extern float *RndTableUniform; ///< Random number table filled with uniform random numbers +#endif + +#ifdef WRITE_MAINBRANCH +extern float *MainBranchMasses; ///< Masses for the main branch history outputs +extern float *MainBranchBinSize; ///< Mass bin sizes for the main branch history outputs +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// MPI Tags // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#define TAG_NTREES 10 ///< MPI tag for the number of trees +#define TAG_NHALOS 11 ///< MPI tag for the number of haloes +#define TAG_NFORESTS 12 ///< MPI tag for the number of forests +#define TAG_NORPHANS 13 ///< MPI tag for the number of orphans +#define TAG_TREEID 14 ///< MPI tag for the tree ID +#define TAG_NHALOSINTREE 15 ///< MPI tag for the number of haloes in a tree +#define TAG_NTREESINFOREST 16 ///< MPI tag for the number of trees in a forest +#define TAG_NHALOSINFOREST 17 ///< MPI tag for the number of haloes in a forest +#define TAG_OFFSETHALOS 18 ///< MPI tag for the offset of haloes in a tree or forest +#define TAG_N 19 ///< MPI tag for the memory allocation +#define TAG_HDATA 20 ///< MPI tag for the halo array +#define TAG_IDS 21 ///< MPI tag for the IDs +#define TAG_SMF 22 ///< MPI tag for the stellar mass function +#define TAG_SMFRED 23 ///< MPI tag for the stellar mass function of quenched galaxies +#define TAG_CSFRD 24 ///< MPI tag for the cosmic star formation rate density +#define TAG_SSFR 25 ///< MPI tag for the specific star formation rate +#define TAG_PROB 26 ///< MPI tag for the probability +#define TAG_REQUEST 27 ///< MPI tag for the pair count requests +#define TAG_COUNT 28 ///< MPI tag for the pair count addition + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Verbose Tags // +/////////////////////////////////////////////////////////////////////////////////////////////////// +#define VERBOSE_MIN 1 ///< Tag for mimimal screen output +#define VERBOSE_ALL 2 ///< Tag for all screen output + +#endif diff --git a/src/clustering.c b/src/clustering.c new file mode 100644 index 0000000..6139f7b --- /dev/null +++ b/src/clustering.c @@ -0,0 +1,1106 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File clustering.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file clustering.c +/// \brief Contains functions that compute the two-point correlation function and its projection +/// +/// This file contains all functions that are needed to compute the two-point galaxy correlation +/// function xi(r) and the projected galaxy correlation function wp(rp). For this a kd-tree is +/// built which is then used to find pairs in parallel for each univere. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/*! \brief This function computes the projected correlation functions + * + * The function first allocates memory for a an array that stores the properties of all galaxies on this + * task that are used for the correlation functions. Going through all haloes on this task the galaxy + * array is filled with positions, masses and SFRs. If the memory is not enough more memory is allocated + * dynamically. Using the galaxy arrays on each task the projected correlation functions in each stellar + * mass bin is computed. + */ +int compute_wp(void) +{ + int i, j; + int igalaxy = 0; + int ngaltot = NALLOCATION; + struct galaxy *localgal = emalloc("LocalGalaxies", ngaltot * sizeof(struct galaxy)); + + //Go through all haloes on this task + for (i = 0; i < Nhalos; i++) + { + //Check if the galaxy is above the minimum mass and below the maximum mass for clustering + if (H[i].iscale == All.Wpscale && H[i].mstar >= All.wpmmin && H[i].mstar < All.wpmmax) + { + //Write all galaxy properties to a local array (localgal) + localgal[igalaxy].x[0] = H[i].pos[0]; + localgal[igalaxy].x[1] = H[i].pos[1]; + localgal[igalaxy].x[2] = H[i].pos[2]; + localgal[igalaxy].mass = H[i].mstar; + localgal[igalaxy].sfr = H[i].sfr; + igalaxy++; + //If the allocated array becomes too small we reallocate + if (igalaxy >= ngaltot) + { + ngaltot += NALLOCATION; + localgal = (struct galaxy *) erealloc(localgal, ngaltot * sizeof(struct galaxy)); + } + } + } + + //Record the total number of galaxies for clustering on this task + ngaltot = igalaxy; + + //For each mass bin determine the projected correlation function using the local galaxy array + for (j = 0; j < All.Nwpset; j++) compute_wp_bin(j,ngaltot,localgal); + + //Free the galaxy array + efree(localgal); + + return 1; + +} + + +/*! \brief This function computes the projected correlation functions + * + * The function first collects all galaxies in this stellar mass bin from the local galaxy array. Then + * the kd-tree is built. The radii for the 3d-correlation function are set logarithmically from + * \f$ r_\mathrm{min} \f$ to \f$ \sqrt{r_\mathrm{max}^2 + \pi_\mathrm{max}^2} \f$, where + * \f$ r_\mathrm{min} \f$ is the smallest radius of the corresonding observed projected correlation + * function, and \f$ r_\mathrm{max} \f$ is defined by the maximum of the largest radius of the corresonding observed + * projected correlation function and #WP_RMAX times the box length: \f$ r_\mathrm{max} = \mathrm{min} + * (r_\mathrm{p, max}, \mathrm{WP\_RMAX} \cdot L_\mathrm{box})\f$. + * + * The 3d-auto-correlation function is then computed as \f[ \xi(r) = \frac{\mathrm{DD}(r)}{\mathrm{RR}(r)} -1 ,\f] + * where \f$ \mathrm{DD}(r)\f$ is calculated using the parallel pair counter funcion #tree_pairs, and + * \f$ \mathrm{RR}(r) \f$ is given by the natural estimator (removing the double counting): \f[ \mathrm{RR}(r) = + * \frac{N(N-1)}{2}\frac{V}{L_\mathrm{box}^3} = N(N-1) \frac{2\pi (r^2 \mathrm{d}r + + * \mathrm{d}r/12)}{L_\mathrm{box}^3} , \f] where V is the volume of the radial bin with width + * \f$ \mathrm{d}r \f$, and \f$ N \f$ is the number of galaxies in this mass bin. + * + * Next, the computed correlation function is interpolated using #WP_RBINS_INT bins. Finally the projected + * correlation function at each observed bin is calculated: \f[ w_\mathrm{p} (r_\mathrm{p}) = + * \int_0^{\pi_\mathrm{max}} 2 \xi(r_\mathrm{p},\pi) \mathrm{d}\pi = 2 \int_{r_\mathrm{p}}^{\sqrt{r_\mathrm{p}^2 + * + \pi_\mathrm{max}^2}} \frac{r \xi(r)}{\sqrt{r^2-r_\mathrm{p}^2}} \; \mathrm{d}r , \f] where \f$ + * \pi_\mathrm{max} \f$ is the maximum distance along the line of sight. + * + * \param iwpbin The index of the stellar mass bin of the projected correlation functions + * \param ngaltot The number of galaxies on this task in the array localgal + * \param *localgal Local galaxy array that holds the properties of all galaxies that are used for correlation functions + */ +int compute_wp_bin(int iwpbin, int ngaltot, struct galaxy *localgal) +{ + int i, j, ngal, nspline; + float rmin, rmax, dlogr, logr, r1, r2, norm, rr, dd, splinemin, splinemax; + float *r,*xi,*xierr; + double *x,*ya,*yb; + struct kd_tree tree; + gsl_spline *splineX, *splineE; + gsl_interp_accel *xacc; + + //Get all galaxies in the corresponding stellar mass bin on every task + ngal = collect_galaxies(iwpbin,ngaltot,localgal); + //There are no pairs for 1 or 0 galaxies (and negative ngal states an error) + if (ngal <= 1) + { //Set all model values to zero and all errors to a large number + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 1.e10; + } + if (ngal >= 0) efree(Galpos); + return 0; + } + + //Construct the kd-tree for the galaxies in this mass bin (return 0 if not possible) + if (!(construct_tree(&tree, Galpos, ngal, TREEDIM))) + { //Set all model values to zero and all errors to a large number + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 1.e10; + } + efree(tree.nodes); + efree(Galpos); + return 0; + } + + //Check if we have enough memory left - otherwise return 0 + if ((long long)((3*WP_RBINS*sizeof(float)) * sizeof(struct kd_pos)) > (long long)(FreeBytes)) + { //Set all model values to zero and all errors to a large number + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 1.e10; + } + efree(tree.nodes); + efree(Galpos); + return 0; + } + + //Allocate the radius, correlation function, and correlation function error arrays + r = emalloc("RADIUS", WP_RBINS * sizeof(float)); + xi = emalloc("XI ", WP_RBINS * sizeof(float)); + xierr = emalloc("XI_ERR", WP_RBINS * sizeof(float)); + + //Set the minimum radius, maximum radius, log bin size, and the normalisation for the correlation function + rmin = Wp[WpSet[iwpbin].offset].obs_x; + rmax = sqrt(min(sqr(Wp[WpSet[iwpbin].offset+WpSet[iwpbin].ndata-1].obs_x),sqr(WP_RMAX*All.Lbox)) + sqr(WpSet[iwpbin].cut)); + dlogr = (log10(rmax)-log10(rmin))/(WP_RBINS-1); + norm = 2.0 * M_PI / 3.0 / cub(All.Lbox); + + //Go through all radial bins (double counting accounted for in both dd and rr) + for (i = 0; i < WP_RBINS; i++) + { + //Set the current log radius, radius, and bin edges + logr = log10(rmin) + (float)(i) * dlogr; + r[i] = pow(10.0, logr); + r1 = pow(10.0, log10(rmin)+((float)(i)-0.5)*dlogr); + r2 = pow(10.0, log10(rmin)+((float)(i)+0.5)*dlogr); + //Compute the random pair count from the natural estimator + rr = norm * (float)(ngal * (ngal-1)) * (cub(r2) - cub(r1)); + //Compute the pair count using the parallel version of the dualtree algorithm + dd = (float)(tree_pairs(tree,Galpos,r1,r2)); + //Get the 3d auto-correlation function + xi[i] = dd/rr - 1.0; + //Set the poisson error for the correlation function + if (dd > 0) xierr[i] = xi[i]/sqrt(dd); + else + { //If the correlation function is zero or negative (error) set it to zero and its error to 1e10 + xi[i] = 0.0; + xierr[i] = 1.0e10; + } + } + + //Check if we have enough memory left - otherwise return 0 + if ((long long)((3*WP_RBINS*sizeof(float)) * sizeof(struct kd_pos)) > (long long)(FreeBytes)) + { //Set all model values to zero and all errors to a large number + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 1.e10; + } + efree(xierr); + efree(xi); + efree(r); + efree(tree.nodes); + efree(Galpos); + return 0; + } + + //Allocate arrays for logarithmic spline interpolation + x = emalloc("X", WP_RBINS * sizeof(double)); + ya = emalloc("YA", WP_RBINS * sizeof(double)); + yb = emalloc("YB", WP_RBINS * sizeof(double)); + + //Initialise the number of points for the spline interpolation + nspline = 0; + splinemin = 1.e30; + splinemax = -1.e30; + //Set the interpolation arrays to the logarithmic radius, xi and xi error + for (i = 0; i < WP_RBINS; i++) + { //Check if the correlation function is positive + if (xi[i] > 0) + { //Set the interplotation to logarithmic + x[nspline] = log10(r[i]); + ya[nspline] = (double)(log10(xi[i])); + yb[nspline] = (double)(log10(xierr[i])); + //Determine the minimum and maximum radius using all valid points + if (x[nspline] < splinemin) splinemin = x[nspline]; + if (x[nspline] > splinemax) splinemax = x[nspline]; + nspline++; + } + } + + //If there are too few points to create the splines + if (nspline < 3) + { //Set all model values to zero and all errors to a large number + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 1.e10; + } + //Free all arrays + efree(yb); + efree(ya); + efree(x); + efree(xierr); + efree(xi); + efree(r); + efree(tree.nodes); + efree(Galpos); + //And return + return 0; + } + + //Set the logarithmic bin size and allocate the splines and acceleration array + dlogr = (log10(rmax)-log10(rmin))/(WP_RBINS_INT-1); + splineX = gsl_spline_alloc(gsl_interp_cspline,nspline); + splineE = gsl_spline_alloc(gsl_interp_cspline,nspline); + xacc = gsl_interp_accel_alloc(); + + //Initialise the splines for the correlation function and its errors + gsl_spline_init(splineX, x, ya, nspline); + gsl_spline_init(splineE, x, yb, nspline); + + //Go through all radii, evaluate the splines and go back to non-logarithmic values + for (i = WP_RBINS_INT * iwpbin; i < WP_RBINS_INT * (iwpbin+1); i++) + { //Set the logarithmic radius + Radius[i] = log10(rmin) + (float)(i - WP_RBINS_INT * iwpbin) * dlogr; + //Check if the radius is in the interpolation range + if (Radius[i] > splinemin && Radius[i] < splinemax) + { //If it is evaluate the splines and go back to non-logarithmic values + Modelxi[i] = gsl_spline_eval(splineX, Radius[i], xacc); + Modelxierr[i] = gsl_spline_eval(splineE, Radius[i], xacc); + Modelxi[i] = pow(10.0, Modelxi[i]); + Modelxierr[i] = pow(10.0, Modelxierr[i]); + } + else + { //Otherwise we can't evaluate this radius and set the correlation function to zero and its error to 1e10 + Modelxi[i] = 0.0; + Modelxierr[i] = 1.e10; + } + //Go back to non-logarithmic radii + Radius[i] = pow(10.0, Radius[i]); + } + + // Integrate xi to get projected correlation function wp + for (i = WpSet[iwpbin].offset; i < WpSet[iwpbin].offset + WpSet[iwpbin].ndata; i++) + { //Set the initial values to zero + Wp[i].mod_y = 0.0; + Wp[i].mod_sigma = 0.0; + for (j = WP_RBINS_INT * iwpbin; j < WP_RBINS_INT * (iwpbin+1); j++) + { //Integrate from rp to sqrt(rp^2+pimax^2) + if (Radius[j] > Wp[i].obs_x && sqr(Radius[j]) <= sqr(WpSet[iwpbin].cut) + sqr(Wp[i].obs_x)) + { //Logarithmic integration, hence the factor log(10) + Wp[i].mod_y += 2.0 * log(10.) * sqr(Radius[j]) * dlogr * Modelxi[j] / sqrt(sqr(Radius[j])-sqr(Wp[i].obs_x)); + Wp[i].mod_sigma += 2.0 * log(10.) * sqr(Radius[j]) * dlogr * Modelxierr[j] / sqrt(sqr(Radius[j])-sqr(Wp[i].obs_x)); + } + } + } + + //Free gsl structures + gsl_interp_accel_free(xacc); + gsl_spline_free(splineE); + gsl_spline_free(splineX); + + //Free all arrays + efree(yb); + efree(ya); + efree(x); + + efree(xierr); + efree(xi); + efree(r); + + //Free the tree nodes and the position array + efree(tree.nodes); + efree(Galpos); + + return 1; + +} + + +/*! \brief This function collects all galaxies in a stellar mass bin and copies them to all tasks + * + * The function first counts the number of galaxies on each task that are in this mass bin. This number is + * then sent by each worker to the master task, where the numbers are summed up and sent back to the workers. + * Each task allocates the global positions array and a helper array to send data. The positions of all galaxies + * in this mass bin are then sent to the master task using the helper array, where they are stored in the + * global positions array. Finally the global positions array is sent back to each worker, and the total + * number of galaxies in this mass bin is returned. + * + * \param iwpbin The index of the stellar mass bin of the projected correlation functions + * \param ngaltot The number of galaxies on this task in the array localgal + * \param *localgal Local galaxy array that holds the properties of all galaxies that are used for correlation functions + */ +int collect_galaxies(int iwpbin, int ngaltot, struct galaxy *localgal) +{ + + int i, j, task, ngalaxies, nthistask, nrecv, offset, Nmax; + struct kd_pos *possend; + MPI_Status status; + + //Initialise the number of galaxies in this bin to zero + nthistask = 0; + //Go through all galaxies in the local array + for (i = 0; i < ngaltot; i++) + { + //If they are in this mass bin increment the counter + if (localgal[i].mass >= WpSet[iwpbin].min && localgal[i].mass < WpSet[iwpbin].max) + nthistask++; + } + + //Initialise the total number of galaxies in this mass bin with the number on this task + ngalaxies = nthistask; + Nmax = nthistask; + + //Send the number of galaxies on each task to the master task + if (ThisTask != MasterTask) + //Each worker task sends nthistask + MPI_Ssend(&nthistask, 1, MPI_INT, MasterTask, TAG_NHALOS, MPI_COMM_WORLD); + else + { + //The Master task goes through all workers + for (task = MasterTask + 1; task < MasterTask + All.NTaskPerUniverse; task++) + { + //Receive the number of galaxies on this worker and increment the total number + MPI_Recv(&nrecv, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD, &status); + ngalaxies += nrecv; + if (nrecv > Nmax) Nmax = nrecv; + } + } + + //Send the total number of galaxies back to the workers + if (ThisTask != MasterTask) + //Each worker task receives the total number of galaxies + MPI_Recv(&ngalaxies, 1, MPI_INT, MasterTask, TAG_NHALOS, MPI_COMM_WORLD, &status); + else + { + //The Master task go through all workers and sends them the total number of galaxies + for (task = MasterTask + 1; task < MasterTask + All.NTaskPerUniverse; task++) + MPI_Ssend(&ngalaxies, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD); + } + + //Check if we have enough memory left - otherwise return -1 + if ((long long)((ngalaxies+Nmax) * sizeof(struct kd_pos)) > (long long)(FreeBytes)) return -1; + + //Allocate the array holding the positions of all galaxies in this mass bin and a helper array to send data + Galpos = emalloc("KDPositions", ngalaxies * sizeof(struct kd_pos)); + if (Nmax > 0) possend = emalloc("PosSend", Nmax * sizeof(struct kd_pos)); + + //The master task first writes all local galaxies that are in this bin to the position array + if (ThisTask == MasterTask) + { //Go through the local galaxy array + for (i = 0, j = 0; i < ngaltot; i++) + { //If the galaxy has the right stellar mass + if (localgal[i].mass >= WpSet[iwpbin].min && localgal[i].mass < WpSet[iwpbin].max) + { //Write it to the position array and increment the counter + Galpos[j].x[0] = localgal[i].x[0]; + Galpos[j].x[1] = localgal[i].x[1]; + Galpos[j].x[2] = localgal[i].x[2]; + j++; + } + } + // Initialise the offset in the position array to the number of galaxies that have just been copied + offset = nthistask; + } + + //Each worker task writes all local galaxies that are in this bin to the helper array and sends it to the master + if (ThisTask != MasterTask) + { //Go through the local galaxy array + for (i = 0, j = 0; i < ngaltot; i++) + { //If the galaxy has the right stellar mass + if (localgal[i].mass >= WpSet[iwpbin].min && localgal[i].mass < WpSet[iwpbin].max && Nmax > 0) + { //Write it to the helper array and increment the counter + possend[j].x[0] = localgal[i].x[0]; + possend[j].x[1] = localgal[i].x[1]; + possend[j].x[2] = localgal[i].x[2]; + j++; + } + } + //Send the number of galaxies in the helper array and then the helper array to the master task + MPI_Ssend(&nthistask, 1, MPI_INT, MasterTask, TAG_NHALOS, MPI_COMM_WORLD); + MPI_Ssend(possend, nthistask * sizeof(struct kd_pos), MPI_BYTE, MasterTask, TAG_HDATA, MPI_COMM_WORLD); + } + else + { //The master task goes through all worker tasks and receives the number of galaxies and the helper array + for (task = MasterTask + 1; task < MasterTask + All.NTaskPerUniverse; task++) + { //Receive the number of galaxies and the helper array + MPI_Recv(&nrecv, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD, &status); + MPI_Recv(possend, nrecv * sizeof(struct kd_pos), MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD, &status); + //Write all positions in the helper array to the global positions array (using the right offset) + for (i = offset, j = 0; j < nrecv; i++, j++) Galpos[i] = possend[j]; + //Increment the offset by the number of galaxies just received and copied + offset += nrecv; + } + } + + //Free the helper array + if (Nmax > 0) efree(possend); + + //Now all worker arrays receive the total global positions array from the master task + if (ThisTask != MasterTask) + MPI_Recv(Galpos, ngalaxies * sizeof(struct kd_pos), MPI_BYTE, MasterTask, TAG_HDATA, MPI_COMM_WORLD, &status); + else + { //The master task goes through all workers and sends them the total global positions array + for (task = MasterTask + 1; task < MasterTask + All.NTaskPerUniverse; task++) + MPI_Ssend(Galpos, ngalaxies * sizeof(struct kd_pos), MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD); + } + + //Return the total number of galaxies in this bin + return ngalaxies; +} + + + + +/*! \brief This function constructs a kd-tree from an array of positions + * + * The function first allocates memory for the worst-case scenario, which is a maximally imbalanced tree. + * Each node has two integers, i0 and i1, which point either to the child nodes, or if the node is a leaf + * to the index of the first and last object in the position array. The initial count for each node is set + * to zero. Using the helper structure kd_build_task that holds the information about the objects on each node + * the nodes are split. For each node the bounding box is determined, and from this the widest dimension is + * found. The node is then split in this dimension at the centre. The splitting is stopped with a leaf if + * either the node has left fewer than #WP_NLEAF_MIN objects, or the widest dimension is smaller than + * #WP_NODE_WIDTH_MIN times the box length. Once the tree has been built, the minimum starting level (where + * there is at least one node per task), the maximum starting level (up to where nodes exist), and the mean + * level are calculated. Finally it is recursively determined how many nodes there are up to the starting level. + * + * + * \param *tree The kd-tree that is being built + * \param *pos The array of positions that is used to build the kd-tree + * \param count The number of objects that the tree contains + * \param dim The dimension of the tree + */ +int construct_tree(struct kd_tree *tree, struct kd_pos *pos, int count, int dim) +{ + + int i, j, i0, i1, inode, n, widestdim, result; + float maxwidth; + struct kd_build_task task, task0, task1; + + //If there are no objects return zero + if (count == 0) return 0; + + //Set the tree's dimensionality and number of objects + tree->dim = dim; + tree->count = count; + + //Set the maximum number of nodes that will be needed if the tree is maximally imbalanced + const int node_count_max = max(2 * (count - WP_NLEAF_MIN) + 1, 1); + + //Allocate all nodes that may be needed + tree->nodes = emalloc("KD-TREE", (node_count_max) * sizeof(struct kd_node)); + + //Set all initial node values + for (i = 0; i < node_count_max; i++) + { + tree->nodes[i].i0 = -1; + tree->nodes[i].i1 = -1; + tree->nodes[i].leaf = 0; + tree->nodes[i].cnt = 0; + } + + //Set the starting node index (beyond the root) + inode = 1; + + //Define the stack size used to create the tree (typically log2(count) is needed). Allow for tolerance. + const int stack_size = (int)(max(log2((double)(count)) / (1.0 - 2.0 * ALLOC_TOLERANCE),1.0)); + + //Declare and allocate the helper task array tasks with stack_size elements + struct kd_build_task tasks[stack_size]; + //Initialise the helper tasks (use root values for all). + for (i = 0; i < stack_size; i++) + { + tasks[i].first = 0; + tasks[i].last = count - 1; + tasks[i].node = 0; + } + + //Initialise the current task to zero + int current_task = 0; + + //Loop over all nodes until all nodes are leaves + do + { + //To simplify the code set task to the current task and n to the current node index + task = tasks[current_task]; + n = tasks[current_task].node; + + //Set the initial bounding box limits + for (j = 0; j < tree->dim; j++) + { + tree->nodes[n].min[j] = pos[task.first].x[j]; + tree->nodes[n].max[j] = pos[task.first].x[j]; + } + + //Determine the number of objects in this node + tree->nodes[n].cnt = task.last - task.first + 1; + + //Go through all particles in the node and determine the bounding box + for (i = task.first; i <= task.last; i++) + for (j = 0; j < tree->dim; j++) + { + if (pos[i].x[j] <= tree->nodes[n].min[j]) tree->nodes[n].min[j] = pos[i].x[j]; + if (pos[i].x[j] >= tree->nodes[n].max[j]) tree->nodes[n].max[j] = pos[i].x[j]; + } + + //Initialise the default widest dimension as zero, and the maximum width as zero + widestdim = 0; + maxwidth = 0.0; + //Go through all dimensions + for (j = 0; j < tree->dim; j++) + { + //If the width of this dimension is wider than the currently stored value + if (tree->nodes[n].max[j] - tree->nodes[n].min[j] > maxwidth) + { + //Set the maximum width to the width in this dimension, and set the widest dimension to this one + maxwidth = tree->nodes[n].max[j] - tree->nodes[n].min[j]; + widestdim = j; + } + } + + //Stop the splitting if we have a leaf, i.e. the number of particles is lower than WP_NLEAF_MIN + //or the maximum width is smaller than WP_NODE_WIDTH_MIN times the box size + if (task.last - task.first < WP_NLEAF_MIN || maxwidth < WP_NODE_WIDTH_MIN * All.Lbox) + { + //Set the indices i0 and i1 to the first and last object in the node, and set leaf to 1 + tree->nodes[n].i0 = task.first; + tree->nodes[n].i1 = task.last; + tree->nodes[n].leaf = 1; + //As this task resulted in a leaf quit it and continue with the previous node/task + current_task--; + continue; + } + + //Set the split value to half the bounding box size (in the widest dimension) + const float splitvalue = (tree->nodes[n].min[widestdim] + tree->nodes[n].max[widestdim]) / 2.0; + + //Partition the object array such that there are only positions in the widest dimension that are + //smaller than the split value in the first part and only positions that are larger in the second + //part. The index of the first particle in the second part is given by k. + int k = partition(pos, task.first, task.last, splitvalue, widestdim); + + // Set the index of the left child node to i0 and the index of the right child node to i1 + i0 = inode; + i1 = i0 + 1; + tree->nodes[n].i0 = i0; + tree->nodes[n].i1 = i1; + //Set the values for the left child node (i0) from first to k-1 + task0.first = task.first; + task0.last = k - 1; + task0.node = i0; + //Set the values for the right child node (i1) from k to last + task1.first = k; + task1.last = task.last; + task1.node = i1; + + //Let the next task deal with the left child node (task0) and increment the node index and current task + tasks[current_task] = task0; + inode++; + current_task++; + //If the task index is larger than the stack size the tree was more imbalanced than assumed, so return zero + if (current_task >= stack_size) + { + if (ThisTask == MasterTask) + printf("KD-tree for universe %d cannot be built. Problem with the stack size!\n",MasterTask/All.NUniverses); + return 0; + } + //Let the next task deal with the left child node (task0) and increment the node index + tasks[current_task] = task1; + inode++; + + //Loop until the current task number is negative, i.e. all tasks have been resolved + } while (current_task != -1); + + //Set the number of nodes to the final node index and reallocate the nodes to the needed size + const int node_count = inode; + tree->nodes = erealloc(tree->nodes, node_count * sizeof(struct kd_node)); + tree->node_count = node_count; + + //Define the intitial minimum level as the log2 of the number of tasks per universe + tree->lmin = (int)(log2(All.NTaskPerUniverse)); + //Define the intitial maximum level as the log2 of the number of objects in the tree + tree->lmax = (int)(log2(tree->node_count)) - 2; + //Define the starting level for the parallel pair counting as the intermediate level between min and max + tree->midlevel = (int)(0.5 * (float)(tree->lmin) + 0.5 * (float)(tree->lmax)); + + //Loop until the number of starting nodes is at least the number of tasks per universe + do + { + //Initialise the counter + i = 0; + //Compute the number of nodes up the the starting level + result = find_nodes(*(tree), &i, 0, tree->midlevel, 0, &i, 0); + //If the starting nodes could not be determined return zero + if (result == 0) return 0; + //If this number is lower than the number of tasks per universe increase the staring level by and repeat + if (i < All.NTaskPerUniverse) (tree->midlevel)++; + //Loop until the number of nodes at the starting level is at least the number of tasks per universe + } while (i < All.NTaskPerUniverse && tree->midlevel <= tree->lmax); + + //Store the number nodes at the starting level + tree->nstartnodes = i; + + //If all went fine return 1 + return 1; +} + + +/*! \brief This function identifies all starting nodes for the parallel pair counting + * + * This function recursively determines the number of nodes up to a specific level l, and if the flag + * save is set to 1, writes all node indices to the array nodeindex. It checks if the current node is + * either at the required level or the node is a leaf. In this case it increments the counter and if + * needed writes the index to nodeindex. Otherwise is continues one level further down. + * + * \param tree kd-tree that is being used to count the pairs + * \param *nodeindex Array containing all starting nodes + * \param inode Index of the current node + * \param l Level at which the starting nodes are located + * \param level Level of the current node + * \param *counter Counter that stores the total number of nodes at the level l and the leaves above + * \param save Flag that indicates if the array containing the starting nodes will be filled (1) or not (0) + */ +int find_nodes(struct kd_tree tree, int *nodeindex, int inode, int l, int level, int *counter, int save) +{ + //The resulting values for the left and right nodes + int result1, result2; + + //If we are at the desired level or hit a leaf before add this node to the array (if save=1) and increment the counter - then return 1 (success) + if (level == l || tree.nodes[inode].leaf == 1) + { + //if (ThisTask==0) printf("%d %d %d\n",(*counter),inode,save); + if (save) nodeindex[(*counter)] = inode; + (*counter)++; + return 1; + } + //If we are at a level before that we go down the left and right child nodes with level+1 + else + { + result1 = find_nodes(tree, nodeindex, tree.nodes[inode].i0, l, level+1, counter, save); + result2 = find_nodes(tree, nodeindex, tree.nodes[inode].i1, l, level+1, counter, save); + return result1 * result2; + } + + //We should never be here - if yes return zero (failure) + return 0; +} + + +/*! \brief This function partitions the arrays of positions around the pivot value + * + * The position array pos is partitioned such that all positions along the dimension dim that are smaller + * than the specified pivot value are moved to the first half of the array, and all positions in that dimension + * that are larger are moved to the second half. The function returns the index of the first element of the + * second half, i.e. the element at this index and all following elements are larger than the pivot index. + * Both the first and the second halves are otherwise unordered. + * + * \param *pos The array of positions that is used to build the kd-tree + * \param left First element of the array to do the partitioning + * \param right Last element of the array to do the partitioning + * \param pivotValue Pivot value used to partition the array (smaller elements are to the left, larger elements are to the right) + * \param dim The dimension of the position that is used for the partitioning + */ +int partition(struct kd_pos *pos, int left, int right, double pivotValue, int dim) +{ +//This swap function exchanges two elements in the position array +#define SWAP(a,b) {struct kd_pos swap=(a); (a)=(b); (b)=swap;} + int i; + //We start at the left index + int storeIndex = left; + //Now we move through all elements of the array from left to right + for (i = left; i <= right; i++) + { + //If the position along the chosen dimension is smaller than the pivot value + if (pos[i].x[dim] < pivotValue) + { + //Swap the position at the store index with the current position and increment the store index + SWAP(pos[storeIndex],pos[i]); + storeIndex++; + } + } + //Return the store index where all elements to the left are smaller than the pivot value and the element at the index and all elements on the right are larger + return storeIndex; +//Undefine the swap function +#undef SWAP +} + + +/*! \brief This function computes the squared minimum distance between two nodes + * + * First the mean position of both nodes along each dimension is determined. If the first node has a larger + * mean position, the distance is determined as the difference between the maximum value of the bounding box + * of node 1 and the minimum of the bounding box of node 2, or by the difference between the minimum value of + * the bounding box of node 1 and the maximum of the bounding box of node 2, whatever is smaller (periodic + * boundary conditions are taken into account). If the first node has a smaller mean position, the distance is + * determined as the difference between the maximum value of the bounding box of node 2 and the minimum of the + * bounding box of node 1, or by the difference between the minimum value of the bounding box of node 2 and the + * maximum of the bounding box of node 1, whatever is smaller. The squared distances are added for each dimension + * and returned. + * + * \param node1 The first node in the distance computation + * \param node2 The second node in the distance computation + * \param dim The dimensionality of the kd-tree + */ +float mindist2_node_node(struct kd_node node1, struct kd_node node2, int dim) +{ + int i; + float x1,x2,m1,m2,d; + //Initialise the distance to zero + d = 0.0; + //Go through all dimensions + for (i = 0; i < dim; i++) + { + //Compute the positions of the box centres + m1 = 0.5 * node1.min[i] + 0.5 * node1.max[i]; + m2 = 0.5 * node2.min[i] + 0.5 * node2.max[i]; + //If the central position of the first box is larger than the second one + if (m1 >= m2) + { + //Compute the distance between the max of node 1 and the min of node 2 (periodic boundaries) + x1 = (node1.max[i] - node2.min[i]); + if (x1 < 0.0) x1 = 0.0; + if (x1 > 0.5 * All.Lbox) x1 = All.Lbox - x1; + //Compute the distance between the min of node 1 and the max of node 2 (periodic boundaries) + x2 = (node1.min[i] - node2.max[i]); + if (x2 < 0.0) x2 = 0.0; + if (x2 > 0.5 * All.Lbox) x2 = All.Lbox - x2; + } + //If the central position of the second box is larger than the first one + else + { + //Compute the distance between the max of node 2 and the min of node 1 (periodic boundaries) + x1 = (node2.max[i] - node1.min[i]); + if (x1 < 0.0) x1 = 0.0; + if (x1 > 0.5 * All.Lbox) x1 = All.Lbox - x1; + //Compute the distance between the min of node 2 and the max of node 1 (periodic boundaries) + x2 = (node2.min[i] - node1.max[i]); + if (x2 < 0.0) x2 = 0.0; + if (x2 > 0.5 * All.Lbox) x2 = All.Lbox - x2; + } + if (x1*x1 < x2*x2) d += sqr(x1); + else d += sqr(x2); + } + return d; +} + + +/*! \brief This function computes the squared maximum distance between two nodes + * + * First the mean position of both nodes along each dimension is determined. If the first node has a larger + * mean position, the distance is determined as the difference between the maximum value of the bounding box + * of node 1 and the minimum of the bounding box of node 2 (periodic boundary conditions are taken into account). + * If the first node has a smaller mean position, the distance is determined as the difference between the + * maximum value of the bounding box of node 2 and the minimum of the bounding box of node 1. The squared + * distances are added for each dimension and returned. + * + * \param node1 The first node in the distance computation + * \param node2 The second node in the distance computation + * \param dim The dimensionality of the kd-tree + */ +float maxdist2_node_node(struct kd_node node1, struct kd_node node2, int dim) +{ + int i; + float x,m1,m2,d; + //Initialise the distance to zero + d = 0.0; + //Go through all dimensions + for (i = 0; i < dim; i++) + { + //Compute the positions of the box centres + m1 = 0.5 * node1.min[i] + 0.5 * node1.max[i]; + m2 = 0.5 * node2.min[i] + 0.5 * node2.max[i]; + //If the central position of the first box is larger than the second one + if (m1 >= m2) + { + //If the distance between the centres is smaller than half the box size the maximum distance is the distance between the max of node 1 and the min of node 2 + if (m1 - m2 <= 0.5 * All.Lbox) x = node1.max[i] - node2.min[i]; + //Otherwise the maximum distance is box size minus the distance between the min of node 1 and the max of node 2 + else x = All.Lbox + node2.max[i] - node1.min[i]; + } + //If the central position of the second box is larger than the first one + else + { + //If the distance between the centres is smaller than half the box size the maximum distance is the distance between the max of node 2 and the min of node 1 + if (m2 - m1 <= 0.5 * All.Lbox) x = node2.max[i] - node1.min[i]; + //Otherwise the maximum distance is box size minus the distance between the min of node 2 and the max of node 1 + else x = All.Lbox + node1.max[i] - node2.min[i]; + } + d += x*x; + } + return d; +} + + +/*! \brief This function computes the number of pairs between two nodes + * + * The algorithm follows the method presented in + * Moore et al. (2001). + * The number of pairs between two nodes are counted efficiently by pruning nodes and subnodes if they are + * either completely inside or outside of the distance bin. If the minimum distance of the nodes is larger than + * the inner radius rmin and the maximum distance is smaller than the outer radius rmax the product of the + * counts of the nodes is returned. Otherwise, if both nodes are leaves the number of pairs is computed in a + * brute force way, by computing the distance of each pair and checking if it is in the distance range, in + * which case the pair counter is incremented. If one or more nodes are parent nodes and cannot be pruned + * the function is called recursively for the child nodes and the results are added up and returned. + * + * \param tree The kd-tree that is used to compute the number of pairs + * \param node1 The first node in the pair counting + * \param node2 The second node in the pair counting + * \param *pos Position array of the objects in the tree + * \param rmin2 The squared minimum distance for the pair counting + * \param rmax2 The squared maximum distance for the pair counting + */ +int dualtree(struct kd_tree tree, struct kd_node node1, struct kd_node node2, struct kd_pos *pos, float rmin2, float rmax2) +{ + int i,j,k; + float mindist2, maxdist2, dist2; + int cleft, cright, cnt; + + //Compute the squared minimum distance between the two nodes + mindist2 = mindist2_node_node(node1,node2,tree.dim); + //Return zero if it is larger than the squared maximum distance (this and all subnodes are too far away) + if (mindist2 > rmax2) return 0; + + //Compute the squared maximum distance between the two nodes + maxdist2 = maxdist2_node_node(node1,node2,tree.dim); + //Return zero if it is smaller than the squared minimum distance (this and all subnodes are too close) + if (maxdist2 <= rmin2) return 0; + + //If the nodes are further away than the minimum distance but closer than the maximum distance the number of pairs is given by the product of the particle numbers in each node + if (rmin2 <= mindist2 && mindist2 <= maxdist2 && maxdist2 < rmax2) + return (node1.cnt * node2.cnt); + + //Both nodes are leaves + if (node1.leaf == 1 && node2.leaf == 1) + { + //Initialise the pair counter to zero + cnt = 0; + //Loop through all objects in the first leaf + for (i = node1.i0; i <= node1.i1; i++) + { + //Loop through all objects in the second leaf + for (j = node2.i0; j <= node2.i1; j++) + { + //Compute the distance between the pair of objects (i in node 1 and j in node 2) + dist2 = 0.0; + for (k = 0; k < tree.dim; k++) dist2 += sqr(NEAREST(pos[i].x[k] - pos[j].x[k])); + //If the distance is larger than the minimum and smaller than the maximum increment the pair counter + if (dist2 > rmin2 && dist2 < rmax2) cnt++; + } + } + //Return all computed pairs between the two leaves + return cnt; + } + //If not both nodes are leaves + else + { + //Initialise the number of pairs for the left and right child node of the larger leaf + cleft = cright = 0; + //If the first node is a leaf (i.e. the second node is a parent node) + if (node1.leaf == 1) + { + //Compute the pair counts for the left and right child node of node 2 recursively + cleft = dualtree(tree,tree.nodes[node2.i0],node1,pos,rmin2,rmax2); + cright = dualtree(tree,tree.nodes[node2.i1],node1,pos,rmin2,rmax2); + } + //If the second node is a leaf (i.e. the first node is a parent node) + else if (node2.leaf == 1) + { + //Compute the pair counts for the left and right child node of node 1 recursively + cleft = dualtree(tree,tree.nodes[node1.i0],node2,pos,rmin2,rmax2); + cright = dualtree(tree,tree.nodes[node1.i1],node2,pos,rmin2,rmax2); + } + //Both nodes are parent nodes + else + { + //If node 1 has more objects go further down node 1, otherwise go further down node 2 + if (node1.cnt > node2.cnt) + { + //Compute the pair counts for the left and right child node of node 1 recursively + cleft = dualtree(tree,tree.nodes[node1.i0],node2,pos,rmin2,rmax2); + cright = dualtree(tree,tree.nodes[node1.i1],node2,pos,rmin2,rmax2); + } + //Node 2 has more (or the same number of) objects than node 1 + else + { + //Compute the pair counts for the left and right child node of node 2 recursively + cleft = dualtree(tree,tree.nodes[node2.i0],node1,pos,rmin2,rmax2); + cright = dualtree(tree,tree.nodes[node2.i1],node1,pos,rmin2,rmax2); + } + } + //If an error has occured in the subnodes and a negative value has been returned return -1 as well + if (cleft < 0) return -1; + if (cright < 0) return -1; + //If everything went well return the sum of the left and right child nodes + return cleft+cright; + } + //We should never arrive here, if we do return -1 + return -1; +} + + +/*! \brief This function computes the number of pairs in a given distance bin + * + * This algorithm is a parallelisation of the kd-tree pair counter following the method presented by + * + * Dolence & Brunner (2008). If the minimum level is larger than the maximum level, the starting level + * is smaller than the minimum level, the starting level is larger than the maximum level, or the number of + * tasks per universe is one, the pairs are not searched in parallel and dualtree is called from the root + * nodes. Otherwise the pairs are searched in parallel. First the starting nodes are determined by calling + * find_nodes with the flag save = 1. Then the master task processes request using processRequests, and + * gives starting nodes to worker tasks. If there are currently no requests for work and there are still + * starting nodes left, the master task computes pairs for the next node. If there are no more nodes left + * the master task answers all work requests with a terminate message. The worker tasks request work + * from the master task and once they receive a new node index they compute the pairs for this node. This + * is repeated until a terminate message has been sent by the master task. In the end all counts are + * added up on all tasks using a message ring. The total number of pairs is then returned. + * + * \param tree The kd-tree that is used to compute the number of pairs + * \param *pos Position array of the objects in the tree + * \param rmin The minimum distance for the pair counting + * \param rmax The maximum distance for the pair counting + */ +int tree_pairs(struct kd_tree tree, struct kd_pos *pos, float rmin, float rmax) +{ + int i, listpos, thispos, nextpos, nterm, totcnt, cnt, cntrecv, left, right; + float rmax2 = sqr(rmax); + float rmin2 = sqr(rmin); + MPI_Status status; + MPI_Status statuses[2]; + MPI_Request request[2]; + + //If the starting level (midlevel) is smaller than the minimum level or larger than the maximum level + //Or if the minimum level is larger than the maximum level or if we just have one task + //Return the pair count (not done in parallel) starting from the root node + //Divide by two to avoid double counting + if (tree.lmin > tree.lmax || tree.midlevel < tree.lmin || tree.midlevel > tree.lmax || All.NTaskPerUniverse == 1 || tree.nstartnodes <= 1) + return dualtree(tree,tree.nodes[0],tree.nodes[0],pos,rmin2,rmax2)/2; + + //Set W as the number of nodes at this level + const int W = tree.nstartnodes; + + //Declare and allocate the list of nodes that will be used as starting points for the parallel computation + int nodelist[W]; + for (i = 0; i < W; i++) nodelist[i] = 0; + + //Initialise counter to zero and identify the starting nodes by going down the tree recursively + i = 0; + find_nodes(tree, nodelist, 0, tree.midlevel, 0, &i, 1); + + //Initialise the pair counter to zero + cnt = 0; + + //Each master task now distributes the work and if it has time does some computation as well + if (ThisTask == MasterTask) + { + listpos = All.NTaskPerUniverse - 1; + thispos = 0; + nterm = 0; + //Loop until all worker tasks have terminated + while (nterm < All.NTaskPerUniverse - 1) + { + //Process all requests (if a worker has finished computing pairs for one node give it a new node) + processRequests(&listpos,&nterm,W); + //If there are still uncomputed nodes do some pair counting yourself + if (listpos < W) + { + //If this is not a leaf compute the pairs for both its child nodes and process requests between + if (tree.nodes[nodelist[thispos]].leaf == 0) + { + cnt += dualtree(tree,tree.nodes[tree.nodes[nodelist[thispos]].i0],tree.nodes[0],pos,rmin2,rmax2); + processRequests(&listpos,&nterm,W); + cnt += dualtree(tree,tree.nodes[tree.nodes[nodelist[thispos]].i1],tree.nodes[0],pos,rmin2,rmax2); + } + //If this is a leaf just compute the counts for this node + else + { + cnt += dualtree(tree,tree.nodes[nodelist[thispos]],tree.nodes[0],pos,rmin2,rmax2); + } + //As we have computed pairs for a node increment the index in the node list + listpos++; + //Set the next node to be computed to the just incremented node index + thispos = listpos; + }//Done with computing the current node on the master task + }//Done with looping (all worker tasks have terminated) + }//Done with master task + //If this is a worker task + else + { + //Set the initial node to be computed + nextpos = ThisTask - MasterTask; + //Loop while the next node is positive (if terminated nextpos is -1) + while (nextpos >= 0) + { + //Ask the master task for the next node (send this task's index to the master task) + MPI_Isend(&ThisTask,1,MPI_INT,MasterTask,TAG_REQUEST,MPI_COMM_WORLD,&request[0]); + //Set the node to be computed to the next one in the queue + thispos = nextpos; + //Receive new node that this worker is responsible for + MPI_Irecv(&nextpos,1,MPI_INT,MasterTask,TAG_REQUEST,MPI_COMM_WORLD,&request[1]); + //Compute the pairs for this node + cnt += dualtree(tree,tree.nodes[nodelist[thispos]],tree.nodes[0],pos,rmin2,rmax2); + //Wait for all communication to finish (so we can use the memory again) + MPI_Waitall(2,request,statuses); + }//Done looping (no more nodes to be computed) + }//Done with the worker tasks + + //Initialise the total number of pairs over all tasks with the one on this task + totcnt = cnt; + + //Repeat for all other tasks on this universe + for (i = 1; i < All.NTaskPerUniverse; i++) + { + //Set the left and right tasks (all within their own universe) + left = ThisTask - 1 < MasterTask ? MasterTask + All.NTaskPerUniverse - 1 : ThisTask - 1; + right = ThisTask + 1 > MasterTask + All.NTaskPerUniverse - 1 ? MasterTask : ThisTask + 1; + //Send the count on this task to the task on the left and receive the count from the right + MPI_Sendrecv(&cnt, 1, MPI_INT, left, TAG_COUNT, &cntrecv, 1, MPI_INT, right, TAG_COUNT, MPI_COMM_WORLD, &status); + //Set the count on this task to what has been received from the right + cnt = cntrecv; + //Add this count to the total number of pairs + totcnt += cnt; + } + + //Return half the total number of pairs (to avoid double counting) + return totcnt/2; +} + + +/*! \brief This function processes all requests for work from worker tasks for the pair counting + * + * This function is called by the master task to process all messages and work requests. A worker task sends + * a message containing its task index. If a starting node is left the master task responds with the next + * worknode and increments the position in the node list. If there are no more nodes left the master task sends + * a terminate message. In the end the function checks if there are new work request and if so answers them + * as well until no more requests are present. + * + * \param *listpos The array of starting nodes for the parallel pair counting + * \param *nterm Number of tasks that have already terminated + * \param W Number of starting nodes for the parallel pair counting + */ +void processRequests(int *listpos, int *nterm, int W) +{ + int isMessage,source,Terminate; + MPI_Status status; + //Set the value for the terminate message + Terminate = -1; + //Check if there is a message from a worker task waiting (i.e. a request for work) + MPI_Iprobe(MPI_ANY_SOURCE,TAG_REQUEST,MPI_COMM_WORLD,&isMessage,&status); + //If there is a message + while(isMessage != 0) + { + //Process messages until queue is empty - receive the task index + MPI_Recv(&source,1,MPI_INT,status.MPI_SOURCE,TAG_REQUEST,MPI_COMM_WORLD,&status); + //If there is still a node available + if((*listpos)+1 < W) + { //Increment the node index + (*listpos)++; + //Send the next node in the list to the worker + MPI_Send(listpos,1,MPI_INT,status.MPI_SOURCE,TAG_REQUEST,MPI_COMM_WORLD); + } + else + { //Out of work, send a terminate message and increment the number of terminated workers + MPI_Send(&Terminate,1,MPI_INT,status.MPI_SOURCE,TAG_REQUEST,MPI_COMM_WORLD); + (*nterm)++; + } + //Check if there is a new message from a worker + MPI_Iprobe(MPI_ANY_SOURCE,TAG_REQUEST,MPI_COMM_WORLD,&isMessage,&status); + } + return; +} diff --git a/src/fit.c b/src/fit.c new file mode 100644 index 0000000..e51f252 --- /dev/null +++ b/src/fit.c @@ -0,0 +1,2010 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File fit.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file fit.c +/// \brief Contains functions that fit the model parameters +/// +/// This file contains the functions that fit the parameters of the model given the data. +/// The implemented methods include an affine invariant ensemble sampler MCMC in #aies_mcmc() +/// and the hybrid optimisation method in #optimize_hybrid(). There are also functions that +/// initialise the parameters on each task and that compute the probability on each master task +/// for a given set of parameters. This can be done on multiple universes in parallel. To do this +/// there are several functions that compute \f$\chi^2\f$ for a given statistics. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/*! \brief This function runs the affine invariant ensemble sampler MCMC + * + * The MCMC can be either started (restart==0) or restarted from a stored walker file (restart==1). + * If newly started it first initialises all walkers with parameters according to the specified range + * in the parameter file. Then the probability for each walker is computed for all universes in + * parallel. If restarted all walkers including their parameters and probabilities are read from a + * walker file. In the main MCMC loop the walkers are split into two sets with equal numbers of walkers. + * For each walker a walker from the other set is randomly selected. The walker parameters are then + * 'stretched' along the direction to the other walker and the probability is computed. This is done + * in parallel for all universes. A random number [0,1) is then computed and compared to the acceptance + * probability. If the new walker is accepted it replaces the old one. During each chain step all walkers + * are printed to a file. After each step a walker file is written containing the parameters and + * probabilities for all walkers. The chain is terminated if the predicted ending time for the next + * step is longer then the time limit specified in the parameter file. This algorithm is an implementation + * of the method presented by Goodman & Weare (2010). + * + * \param restart Flag indicating if the MCMC chain is started (0) or restarted from a walker file (1) + */ +void aies_mcmc(int restart) +{ + const int nline = 500; + char outfname[NSTRING], infname[NSTRING], line[nline], *tmp; + int i,j,k,iwalker,jwalker,istart,itask,istep,isave,isplit,doloop,accept,nrun,nfilepar,nfilewalk; + float px,py; + double q,lastlooptime; + double acctot = 0.0; + FILE *ifp,*ofp,*wfp; + MPI_Status status; + gsl_rng *rng_uniform_mcmc; + + //Define the scale parameter for the MCMC + double a = sqrt(All.mcmca); + + //Define the walker array x, the proposed walker array y, and the probability array prob + double *x; //Walkers, Length: Nwalkers*Nparam + double *y; //Updated walker + double *z; //Scale of the proposed step + float *prob; //Current probability for each walker + + //The function can only be called with a restart of 0 (start new chain) or 1 (restart from walker file) + if (restart < 0 || restart > 1) + { + if (ThisTask == 0) printf("Affine invariant ensemble sampler MCMC called with wrong restart flag"); + return; + } + + //Initialise the uniform generator + rng_uniform_mcmc = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_uniform_mcmc, All.Seed); + + //Allocate all arrays + prob = emalloc_movable(&prob, "Prob", All.Nwalkers * sizeof(float)); + x = emalloc_movable(&x, "X", All.Nwalkers * All.Nparam * sizeof(double)); + y = emalloc_movable(&y, "Y", All.Nparam * All.NUniverses * sizeof(double)); + z = emalloc_movable(&z, "Z", All.NUniverses * sizeof(double)); + + //This is the first run + nrun = 0; + + //If this is the first run of the MCMC then set up the run + if (restart == 0) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the MCMC output file + sprintf(outfname,"%s/mcmc%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Print what is done... + printf("%s\n%s Starting affine invariant ensemble sampler MCMC...\n",All.fullline,All.startline); + printf("%s Setting up %d Walkers...\n",All.startline,All.Nwalkers); + + //Print the header for the MCMC steps + print_mcmc_header(); + } + + //Initialization of walkers + init_param(x); + + //Calculate the starting probability for each walker + for (j = 0; j < All.Nwalkers/All.NUniverses; j++) + { + //Compute the probability for each universe + px = lnprob(&x[j*All.NUniverses*All.Nparam]); + //Set the current time + All.timenow = second() - All.starttime; + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the probability to task 0 + MPI_Ssend(&px, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the probability from the other master task + MPI_Recv(&px, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + //Write the probability to the correct index (only valid for task 0) + prob[j*All.NUniverses+itask] = px; + //If this is task 0 write all parameters and the probability to screen and output file + if (ThisTask == 0) + { + printf("%8d %8.1f",j*All.NUniverses+itask,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + printf(" %f\n",prob[j*All.NUniverses+itask]); + + fprintf(ofp,"%f",prob[j*All.NUniverses+itask]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + fprintf(ofp," 0 0 0 %d\n",j*All.NUniverses+itask); + fflush(ofp); + } + }//End loop through master tasks + }//End loop through all walkers + + //Set the start index to zero + istart = 0; + + }//End restart 0 + + //If the chain is restarted from a file with walkers + if (restart == 1) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the walker file + sprintf(infname,"%s/walkers.mcmc.%d.out",All.OutputDir,All.Seed); + if(!(ifp=fopen(infname,"r"))) + { + sprintf(line, "Cannot open file `%s' for reading the walkers.", infname); + endrun(line); + } + + //Get first line of walker file + fgets(line,nline,ifp); + sscanf(line,"#Walkers/Parameters/seed/i %d %d %d %d\n", &nfilewalk, &nfilepar, &nrun, &istart); + + //Sanity Checks + if (nfilewalk != All.Nwalkers) + { + sprintf(line, "The walker file %s contains %d walkers (while the code needs %d)",infname,nfilewalk,All.Nwalkers); + endrun(line); + } + if (nfilepar != All.Nparam) + { + sprintf(line, "The walker file %s contains %d parameters (while the code needs %d)",infname,nfilepar,All.Nparam); + endrun(line); + } + if (nrun < 0) + { + sprintf(line, "The walker file %s has a negative run number (%02d)",infname,nrun); + endrun(line); + } + + //Increment the Run Number + nrun++; + + //Increment the starting step + istart++; + + //Print what is done... + printf("%s\n%s Restarting affine invariant ensemble sampler MCMC...\n",All.fullline,All.startline); + printf("%s Reading %d Walkers from %s...\n",All.startline,All.Nwalkers,infname); + + //Set output file and open file pointer + sprintf(outfname,"%s/mcmc%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Get each line in the file + for (k = 0; k < All.Nwalkers; k++) + { + //Get the new line + fgets(line,nline,ifp); + tmp = strtok(line, " "); + //Get the probability + prob[k] = atof(tmp); + //Get all the parameters for the walker + for (i = 0; i < All.Nparam; i++) + { + tmp = strtok(NULL, " "); + x[k*All.Nparam+i] = atof(tmp); + } + } //End reading walker file + fclose(ifp); + } + + //Re-initialize random numbers + gsl_rng_set(rng_uniform_mcmc, All.Seed + 100 * nrun); + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + } + + //Broadcast the probability array from task 0 + MPI_Bcast(prob, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + + //Define loop iteration variable + istep=istart; + isave=istart*All.Nwalkers; + doloop=1; + + //Inform everyone we are starting with the main MCMC loop + if (ThisTask == 0) printf("%s\n%s Starting main MCMC loop...\n",All.fullline,All.startline); + + //Start main MCMC loop, stop loop after some specified time is reached + while (doloop) + { + //Initialise Loop start time + lastlooptime = second() - All.starttime; + + //Print the header for the MCMC steps + if (ThisTask == 0) print_mcmc_header(); + + //Split the walkers in two sets with the same number of walkers + for (isplit = 0; isplit < 2; isplit++) + { + //Loop through half of the walkers + for (j = 0; j < All.Nwalkers/All.NUniverses/2; j++) + { + //Set the current time + All.timenow = second() - All.starttime; + //Set up new trial walkers for all universes + + for (itask = 0; itask < All.NUniverses; itask++) + { + //Current walker + iwalker = isplit*All.Nwalkers/2 + j*All.NUniverses + itask; + //For walker iwalker, select random walker jwalker from other set + jwalker = (int)((gsl_rng_uniform(rng_uniform_mcmc)+1.0-(double)(isplit))*(double)(All.Nwalkers)*0.5); + //Draw random number from g(z) = (z)^(-1/2) within [1/a,a] + z[itask] = pow((a-1.0)*gsl_rng_uniform(rng_uniform_mcmc)+1.0,2.0)/a; + //Stretch walker iwalker along jwalker to obtain new propasal walker y + for (i = 0; i < All.Nparam; i++) + y[itask*All.Nparam+i] = x[jwalker*All.Nparam+i] + z[itask] * (x[iwalker*All.Nparam+i] - x[jwalker*All.Nparam+i]); + } + + //Broadcast the walker array y from task 0 + MPI_Bcast(y, All.NUniverses * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + //Compute the probability for each universe + py = lnprob(y); + MPI_Barrier(MPI_COMM_WORLD); + + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //Set index of the current walker + iwalker = isplit*All.Nwalkers/2 + j*All.NUniverses + itask; + //Get this walkers previous probability + px = prob[iwalker]; + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the probability to task 0 + MPI_Ssend(&py, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the probability from the other master task + MPI_Recv(&py, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + + //Sanity check if the computed probability is in a sensible range + if (!(py < 0 && py > -1.e10) ) py = -2.e10; + + //Get probability to accept walker and decide whether to accept y + q = ((double)(All.Nparam)-1.0)*log(z[itask])+py-px; + accept = (exp(q) > gsl_rng_uniform(rng_uniform_mcmc)); + + //If the probability is beyond the edge do not accept + if (py < -1.e10) accept = 0; + + //If this is task 0 write all parameters and the probability to screen and output file + if (ThisTask == 0) + { + //If y is accepted set walker k to y and store probability and update accepted fraction + if (accept) + { + for (i = 0; i < All.Nparam; i++) x[iwalker*All.Nparam+i] = y[itask*All.Nparam+i]; + prob[iwalker] = py; + acctot+=1.0; + } + + //Print the walkers to screen + printf("%8d %8.1f",isave,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[iwalker*All.Nparam+i]); + printf(" %f\n",prob[iwalker]); + + //Output to file + fprintf(ofp,"%f",prob[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[iwalker*All.Nparam+i]); + fprintf(ofp," %f %d %d %d\n",acctot/(isave+1),isave,istep,iwalker); + fflush(ofp); + + }//End task 0 + + //Update output index + isave++; + + }//loop through master tasks + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the probability array from task 0 + MPI_Bcast(prob, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + + }//loop through half walkers + }//isplit + + //Only task 0 writes the walker file + if (ThisTask == 0) + { + //Write all current walkers to a file + sprintf(outfname,"%s/walkers.mcmc.%d.out",All.OutputDir,All.Seed); + wfp = fopen(outfname,"w"); + fprintf(wfp,"#Walkers/Parameters/seed/i %d %d %d %d\n",All.Nwalkers,All.Nparam,nrun,istep); + for (iwalker = 0; iwalker < All.Nwalkers; iwalker++) + { + fprintf(wfp,"%f",prob[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(wfp," %f",x[iwalker*All.Nparam+i]); + fprintf(wfp,"\n"); + } + fclose(wfp); + } + + //Update step + istep++; + + //Get time the initialisation loop took + lastlooptime = second() - All.starttime -lastlooptime; + All.timenow = second() - All.starttime; + + //End loop if projected finish time of next loop would be larger than the time limit + if (All.timenow+1.5*lastlooptime>All.timelimit) doloop = 0; + + }//loop + + //Inform everyone we are done with the main MCMC loop + if (ThisTask == 0) printf("%s Done with MCMC...\n",All.startline); + + //Free all arrays and the output file pointer + efree_movable(z); + efree_movable(y); + efree_movable(x); + efree_movable(prob); + if (ThisTask == 0) fclose(ofp); + gsl_rng_free(rng_uniform_mcmc); + +} + + +/*! \brief This function runs the HYBRID optimization method + * + * The method can be either started (restart==0) or restarted from a stored walker file (restart==1). + * If newly started it first initialises all walkers with parameters according to the specified range + * in the parameter file. Then \f$\chi^2\f$ for each walker is computed for all universes in + * parallel. Also the mean starting \f$\chi^2\f$ is computed and stored. If restarted all walkers including + * their parameters and probabilities are read from a walker file. In the main HYBRID loop first the + * mean current \f$\chi^2\f$ over all walkers is computed and broadcast from task 0. The value q (mean current + * \f$\chi^2\f$ over mean starting \f$\chi^2\f$) and the resulting g value are computed. For each walker the p + * value (\f$\chi^2\f$ over mean current \f$\chi^2\f$) and the resulting f value are computed. Then a new proposal + * walker is computed from the current walker its width sigma and the g and f values and the probability + * is computed. This is done in parallel for all universes. A random number [0,1) is then computed and + * compared to the acceptance probability given the current temperature. If the new walker is accepted + * it replaces the old one. During each chain step all walkers are printed to a file. After each step + * a walker file is written containing the parameters and probabilities for all walkers. The chain is + * terminated if the predicted ending time for the next step is longer then the time limit specified + * in the parameter file. This algorithm is an implementation of the method presented by + * Elson et al. (2007). + * + * \param restart Flag indicating if the HYBRID chain is started (0) or restarted from a walker file (1) + */ +void optimize_hybrid(int restart) +{ + +//Define slopes +#ifndef HYBRID_ALPHA +#define HYBRID_ALPHA 0.4 +#endif + +#ifndef HYBRID_BETA +#define HYBRID_BETA 1.0 +#endif + +#ifndef HYBRID_GAMMA +#define HYBRID_GAMMA 1.0 +#endif + + const int nline = 500; + char outfname[NSTRING], infname[NSTRING], line[nline], *tmp; + int i,j,k,iwalker,istart,itask,istep,isave,doloop,accept,nrun,nfilepar,nfilewalk; + float cx,cy,mean_starting_chi2,mean_current_chi2,min_current_chi2,max_current_chi2; + double alpha,q,p,f,g,lastlooptime; + FILE *ifp,*ofp,*wfp; + MPI_Status status; + gsl_rng *rng_uniform_hybrid, *rng_gaussian_hybrid; + + int *ac; //Acceptance counter for each walker + double *x; //Walkers, Length: Nwalkers*Nparam + double *y; //Updated walker + double *sigma; //Width for the chosen parameter space + float *chi2; //Current chi^2 for each walker + + //The function can only be called with a restart of 0 (start new chain) or 1 (restart from walker file) + if (restart < 0 || restart > 1) + { + if (ThisTask == 0) printf("HYBRID optimization method called with wrong restart flag"); + return; + } + + //Allocate all arrays + ac = emalloc_movable(&ac, "AC", All.Nwalkers * sizeof(int)); + chi2 = emalloc_movable(&chi2, "Chi2", All.Nwalkers * sizeof(float)); + sigma = emalloc_movable(&sigma, "Sigma", All.Nparam * sizeof(double)); + x = emalloc_movable(&x, "X", All.Nwalkers * All.Nparam * sizeof(double)); + y = emalloc_movable(&y, "Y", All.Nparam * All.NUniverses * sizeof(double)); + + //This is the first run + nrun = 0; + + // Get a sigma (width) for the chosen parameter space + init_sigma(sigma); + + //Initialise the uniform generator + rng_uniform_hybrid = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_uniform_hybrid, All.Seed); + + //Initialise the gaussian generator + rng_gaussian_hybrid = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_gaussian_hybrid, All.Seed); + + //If this is the first run of the MCMC then set up the run + if (restart == 0) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the MCMC output file + sprintf(outfname,"%s/hybrid%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Print what is done... + printf("%s\n%s Starting HYBRID optimization method...\n",All.fullline,All.startline); + printf("%s Setting up %d Walkers...\n",All.startline,All.Nwalkers); + + //Print the header for the chain steps + print_mcmc_header(); + } + + //Initialization of walkers + init_param(x); + + //Calculate the starting chi^2 for each walker + for (j = 0; j < All.Nwalkers/All.NUniverses; j++) + { + //Compute the probability for each universe + cx = -2.0 * lnprob(&x[j*All.NUniverses*All.Nparam]); + //Set the current time + All.timenow = second() - All.starttime; + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the chi^2 to task 0 + MPI_Ssend(&cx, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the chi^2 from the other master task + MPI_Recv(&cx, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + //Write the chi^2 to the correct index (only valid for task 0) + chi2[j*All.NUniverses+itask] = cx; + //If this is task 0 write all parameters and the chi^2 to screen and output file + if (ThisTask == 0) + { + printf("%8d %8.1f",j*All.NUniverses+itask,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + printf(" %f\n",-0.5*chi2[j*All.NUniverses+itask]); + + fprintf(ofp,"%f",chi2[j*All.NUniverses+itask]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + fprintf(ofp," 0 0 0 0 0 0 0 0 0 %d\n",j*All.NUniverses+itask); + fflush(ofp); + } + }//End loop through master tasks + }//End loop through all walkers + + // Compute mean starting chi2 + mean_starting_chi2 = 0; + for (k = 0; k < All.Nwalkers; k++) mean_starting_chi2 += chi2[k]; + mean_starting_chi2 /= All.Nwalkers; + + // Initialise acceptance counters and starting step + for (k = 0; k < All.Nwalkers; k++) ac[k] = 0; + istart = 0; + + }//End restart 0 + + //If the chain is restarted from a file with walkers + if (restart == 1) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the walker file + sprintf(infname,"%s/walkers.hybrid.%d.out",All.OutputDir,All.Seed); + if(!(ifp=fopen(infname,"r"))) + { + sprintf(line, "Cannot open file `%s' for reading the walkers.", infname); + endrun(line); + } + + //Get first line of walker file + fgets(line,nline,ifp); + sscanf(line,"#Walkers/Parameters/seed/i/mean_starting_chi2/Temperature %d %d %d %d %f %lf\n", &nfilewalk, &nfilepar, &nrun, &istart, &mean_starting_chi2, &All.temperature); + + //Sanity Checks + if (nfilewalk != All.Nwalkers) + { + sprintf(line, "The walker file %s contains %d walkers (while the code needs %d)",infname,nfilewalk,All.Nwalkers); + endrun(line); + } + if (nfilepar != All.Nparam) + { + sprintf(line, "The walker file %s contains %d parameters (while the code needs %d)",infname,nfilepar,All.Nparam); + endrun(line); + } + if (nrun < 0) + { + sprintf(line, "The walker file %s has a negative run number (%02d)",infname,nrun); + endrun(line); + } + if (istart < 0) + { + sprintf(line, "The walker file %s has a negative starting step number (%02d)",infname,istart); + endrun(line); + } + + //Increment the Run Number + nrun++; + + //Increment the starting step + istart++; + + //Print what is done... + printf("%s\n%s Restarting HYBRID optimization method...\n",All.fullline,All.startline); + printf("%s Reading %d Walkers from %s...\n",All.startline,All.Nwalkers,infname); + + //Set output file and open file pointer + sprintf(outfname,"%s/hybrid%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Get each line in the file + for (k = 0; k < All.Nwalkers; k++) + { + //Get the new line + fgets(line,nline,ifp); + tmp = strtok(line, " "); + //Get the chi^2 + chi2[k] = atof(tmp); + //Get all the parameters for the walker + for (i = 0; i < All.Nparam; i++) + { + tmp = strtok(NULL, " "); + x[k*All.Nparam+i] = atof(tmp); + } + //Get acceptance counters for each walker + tmp = strtok(NULL, " "); + ac[k] = atoi(tmp); + } //End reading walker file + fclose(ifp); + } + + //Broadcast the nrun, istart and the temperature from task 0 + MPI_Bcast(&nrun, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&istart, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.temperature, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + //Re-initialize random numbers + gsl_rng_set(rng_uniform_hybrid, All.Seed + 100 * nrun); + gsl_rng_set(rng_gaussian_hybrid, All.Seed + 100 * nrun); + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the acceptance counter array from task 0 + MPI_Bcast(ac, All.Nwalkers, MPI_INT, 0, MPI_COMM_WORLD); + } + + //Broadcast the chi2 array from task 0 + MPI_Bcast(chi2, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + //Broadcast starting state of the chains + MPI_Bcast(&mean_starting_chi2, 1, MPI_FLOAT, 0, MPI_COMM_WORLD); + + //Define loop iteration variable + istep=istart; + isave=istart*All.Nwalkers; + doloop=1; + + //Inform everyone we are starting with the main MCMC loop + if (ThisTask == 0) printf("%s\n%s Starting main loop for HYBRID optimization method at istart = %d...\n",All.fullline,All.startline,istart); + + //Start main MCMC loop, stop loop after some specified time is reached + while (doloop) + { + //Initialise Loop start time + lastlooptime = second() - All.starttime; + + if (ThisTask == 0) + { + //Print the header for the chain steps + print_mcmc_header(); + + // Compute mean/min/max current chi2 + mean_current_chi2 = 0; + min_current_chi2 = max_current_chi2 = chi2[0]; + for (k = 0; k < All.Nwalkers; k++) + { + mean_current_chi2 += chi2[k]; + if (min_current_chi2 > chi2[k]) min_current_chi2 = chi2[k]; + if (max_current_chi2 < chi2[k]) max_current_chi2 = chi2[k]; + } + mean_current_chi2 /= All.Nwalkers; + } + + //Broadcast mean, min and max chi^2 to other tasks + MPI_Bcast(&mean_current_chi2, 1, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(&min_current_chi2, 1, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(&max_current_chi2, 1, MPI_FLOAT, 0, MPI_COMM_WORLD); + + // Check how well current sample is doing wrt to initial sample + q = mean_current_chi2 / mean_starting_chi2; + g = pow(q,HYBRID_ALPHA); + + // Sets the likelihood of accepting based on the current temperature + if (All.temperature > 0.0) alpha = log(((double)(istep))+2.71828182845905) / All.temperature; + else if (All.temperature == 0.0) alpha = 1.e20; + else alpha = -1.0/All.temperature; + + //Loop through the walkers + for (j = 0; j < All.Nwalkers/All.NUniverses; j++) + { + //Set the current time + All.timenow = second() - All.starttime; + //Set up new trial walkers for all universes + + for (itask = 0; itask < All.NUniverses; itask++) + { + //Current walker + iwalker = j*All.NUniverses + itask; + //Get this walkers previous probability + cx = chi2[iwalker]; + // Project chi2 squares to p around [-1:1] and compute f + // If chi2 is larger than mean, p < 0, use linear slope + if (cx > mean_current_chi2) + { + p = ((mean_current_chi2-cx)/(max_current_chi2-mean_current_chi2)); + f = 1.0 - HYBRID_BETA * p; + } else + // Now chi2 is smaller than mean, p > 0, and we use power law decay + { + p = ((mean_current_chi2-cx)/(mean_current_chi2-min_current_chi2)); + f = pow(p + 1.0, -1.0 * HYBRID_GAMMA); + } + + //Stretch walker iwalker along jwalker to obtain new propasal walker y + for (i = 0; i < All.Nparam; i++) + y[itask*All.Nparam+i] = x[iwalker*All.Nparam+i] + sigma[i] * f * g * gsl_ran_gaussian(rng_gaussian_hybrid,1.0); + } + + //Broadcast the walker array y from task 0 + MPI_Bcast(y, All.NUniverses * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + //Compute the probability for each universe + cy = -2.0*lnprob(y); + MPI_Barrier(MPI_COMM_WORLD); + + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //Set index of the current walker + iwalker = j*All.NUniverses + itask; + //Get this walkers previous probability + cx = chi2[iwalker]; + //Compute p and f again for this task + if (cx > mean_current_chi2) + { + p = ((mean_current_chi2-cx)/(max_current_chi2-mean_current_chi2)); + f = 1.0 - HYBRID_BETA * p; + } else + // Now chi2 is smaller than mean, p > 0, and we use power law decay + { + p = ((mean_current_chi2-cx)/(mean_current_chi2-min_current_chi2)); + f = pow(p + 1.0, -1.0 * HYBRID_GAMMA); + } + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the chi^2 to task 0 + MPI_Ssend(&cy, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the chi^2 from the other master task + MPI_Recv(&cy, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + + //Sanity check if the computed chi^2 is in a sensible range + if (!(cy > 0 && cy < 1.e10) ) cy = 2.e10; + + // Check if new step is accepted + accept = (alpha * 0.5 * (cx-cy) > log(gsl_rng_uniform(rng_uniform_hybrid))); + + //If the probability is beyond the edge do not accept + if (cy > 1.e10) accept = 0; + + //If this is task 0 write all parameters and the chi^2 to screen and output file + if (ThisTask == 0) + { + //If y is accepted set walker k to y and store chi^2 and update accepted fraction + if (accept) + { + for (i = 0; i < All.Nparam; i++) x[iwalker*All.Nparam+i] = y[itask*All.Nparam+i]; + cx = cy; + chi2[iwalker] = cx; + ac[iwalker]++; + } + + //Print the walkers to screen + printf("%8d %8.1f",isave,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[iwalker*All.Nparam+i]); + printf(" %f\n",-0.5*chi2[iwalker]); + + //Output to file + fprintf(ofp,"%f",chi2[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[iwalker*All.Nparam+i]); + fprintf(ofp," %f %f %f %f %f %f %f %d %d %d\n",mean_current_chi2,p,q,f,g,alpha,((double)(ac[iwalker]))/((double)(istep+1)),isave,istep,iwalker); + fflush(ofp); + + }//End task 0 + + //Update output index + isave++; + + }//loop through master tasks + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the chi2 array from task 0 + MPI_Bcast(chi2, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + //Broadcast the acceptance counter array from task 0 + MPI_Bcast(ac, All.Nwalkers, MPI_INT, 0, MPI_COMM_WORLD); + + }//loop through walkers + + //Only task 0 writes the walker file + if (ThisTask == 0) + { + //Write all current walkers to a file + sprintf(outfname,"%s/walkers.hybrid.%d.out",All.OutputDir,All.Seed); + wfp = fopen(outfname,"w"); + fprintf(wfp,"#Walkers/Parameters/seed/i/mean_starting_chi2/Temperature %d %d %d %d %f %f\n",All.Nwalkers,All.Nparam,nrun,istep,mean_starting_chi2,All.temperature); + for (iwalker = 0; iwalker < All.Nwalkers; iwalker++) + { + fprintf(wfp,"%f",chi2[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(wfp," %f",x[iwalker*All.Nparam+i]); + fprintf(wfp," %d\n",ac[iwalker]); + } + fclose(wfp); + } + + //Update step + istep++; + + //Get time the initialisation loop took + lastlooptime = second() - All.starttime - lastlooptime; + All.timenow = second() - All.starttime; + + //End loop if projected finish time of next loop would be larger than the time limit + if (All.timenow+1.5*lastlooptime>All.timelimit) doloop = 0; + + }//loop + + //Inform everyone we are done with the main MCMC loop + if (ThisTask == 0) printf("%s Done with HYBRID optimization method...\n",All.startline); + + //Free all arrays and the output file pointer + efree_movable(y); + efree_movable(x); + efree_movable(sigma); + efree_movable(chi2); + efree_movable(ac); + if (ThisTask == 0) fclose(ofp); + gsl_rng_free(rng_gaussian_hybrid); + gsl_rng_free(rng_uniform_hybrid); + +} + + +/*! \brief This function runs the parallel tempering method + * + * The method can be either started (restart==0) or restarted from a stored walker file (restart==1). + * If newly started it first initialises all walkers with parameters according to the specified range + * in the parameter file. Then the probability for each walker is computed for all universes in + * parallel. The fraction of cold walkers with a temperature of T=1 is given by PT_COLD_FRACTION. The + * temperature for the remaining walkers is taken from a log uniform distribution up to the temperature + * given in the parameter file. If restarted all walkers including their parameters, probabilities, + * and temperatures are read from a walker file. In the main PT loop first each walker can swap the + * position and probability with a random other walker of different temperature. Then each walker is + * evolved using the Metropolis-Hastings algorithm, i.e. a new proposal walker is computed given + * the current walker and a proposal sigma. The probability of the new walker is then compared to the + * current one and the acceptance probability at the given temperature is computed. If accepted the + * new position and probability replaces the current one. This is done in parallel for all universes. + * During each chain step all walkers are printed to a file. After each step a walker file is written + * containing the parameters and probabilities for all walkers. The chain is terminated if the predicted + * ending time for the next step is longer then the time limit specified in the parameter file. + * + * \param restart Flag indicating if the PT chain is started (0) or restarted from a walker file (1) + */ +void parallel_tempering(int restart) +{ + +//First define the fraction of cold walkers with T=1 +#ifndef PT_COLD_FRACTION +#define PT_COLD_FRACTION 0.25 +#endif + +//Now define the target acceptance rate for each walker +#ifndef PT_TARGET_ACCEPTANCE +#define PT_TARGET_ACCEPTANCE 0.30 +#endif + +//And define the number of chain steps after which the proposal width sigma is adjusted +#ifndef PT_STEPS_SCALE_ADJUST +#define PT_STEPS_SCALE_ADJUST 10 +#endif + + const int nline = 500; + char outfname[NSTRING], infname[NSTRING], line[nline], *tmp; + int i,j,k,iwalker,istart,itask,istep,isave,doloop,accept,nrun,nfilepar,nfilewalk,ncold; + float px,py; + double xswap, lastlooptime, facc; + FILE *ifp,*ofp,*wfp; + MPI_Status status; + gsl_rng *rng_uniform_pt, *rng_gaussian_pt; + + int *ac; //Acceptance counter for each walker + double *x; //Walkers, Length: Nwalkers*Nparam + double *y; //Updated walker + double *sigma; //Width for the chosen parameter space for each walker + double *scale; //Scale parameter to adjust sigma based on acceptance rate for each walker + float *prob; //Current chi^2 for each walker + float *beta; //Inverse temperature for each walker + + //The function can only be called with a restart of 0 (start new chain) or 1 (restart from walker file) + if (restart < 0 || restart > 1) + { + if (ThisTask == 0) printf("Parallel tempering method called with wrong restart flag"); + return; + } + + //Allocate all arrays + ac = emalloc_movable(&ac, "AC", All.Nwalkers * sizeof(int)); + prob = emalloc_movable(&prob, "Prob", All.Nwalkers * sizeof(float)); + beta = emalloc_movable(&beta, "BetaT", All.Nwalkers * sizeof(float)); + scale = emalloc_movable(&beta, "Scale", All.Nwalkers * sizeof(double)); + sigma = emalloc_movable(&sigma, "Sigma", All.Nparam * sizeof(double)); + x = emalloc_movable(&x, "X", All.Nwalkers * All.Nparam * sizeof(double)); + y = emalloc_movable(&y, "Y", All.NUniverses * All.Nparam * sizeof(double)); + + //This is the first run + nrun = 0; + + // Get a sigma (width) for the chosen parameter space + init_sigma(sigma); + + //Determine the number of cold walkers + ncold = (int)(PT_COLD_FRACTION * (double)(All.Nwalkers)); + + //Initialise the uniform generator + rng_uniform_pt = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_uniform_pt, All.Seed); + + //Initialise the gaussian generator + rng_gaussian_pt = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_gaussian_pt, All.Seed); + + //If this is the first run of the MCMC then set up the run + if (restart == 0) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the MCMC output file + sprintf(outfname,"%s/pt%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Print what is done... + printf("%s\n%s Starting parallel tempering method...\n",All.fullline,All.startline); + printf("%s Setting up %d Walkers...\n",All.startline,All.Nwalkers); + + //Print the header for the chain steps + print_mcmc_header(); + } + + //Set the temperature for each walker + for (k = 0; k < All.Nwalkers; k++) + { + //First the cold walkers + if (k < ncold) beta[k] = 1.0; + //And the temperature ladder + else beta[k] = 1.0 / exp((double)(k-ncold+1) / (double)(All.Nwalkers-ncold) * log(All.temperature)); + } + + //Initialization of walkers + init_param(x); + + //Calculate the starting probability for each walker + for (j = 0; j < All.Nwalkers/All.NUniverses; j++) + { + //Compute the probability for each universe + px = lnprob(&x[j*All.NUniverses*All.Nparam]); + //Set the current time + All.timenow = second() - All.starttime; + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the probability to task 0 + MPI_Ssend(&px, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the probability from the other master task + MPI_Recv(&px, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + //Write the probability to the correct index (only valid for task 0) + prob[j*All.NUniverses+itask] = px; + //If this is task 0 write all parameters and the probability to screen and output file + if (ThisTask == 0) + { + printf("%8d %8.1f",j*All.NUniverses+itask,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + printf(" %f %6.3f 1.0\n",prob[j*All.NUniverses+itask],1./beta[j*All.NUniverses+itask]); + + fprintf(ofp,"%f",prob[j*All.NUniverses+itask]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[(j*All.NUniverses+itask)*All.Nparam+i]); + fprintf(ofp," %6.3f 1.0 0 0 0 %d\n",1./beta[j*All.NUniverses+itask],j*All.NUniverses+itask); + fflush(ofp); + } + }//End loop through master tasks + }//End loop through all walkers + + // Initialise acceptance counters, scale adjustments and starting step + for (k = 0; k < All.Nwalkers; k++) + { + ac[k] = 0; + scale[k] = 1.0; + } + istart = 0; + + }//End restart 0 + + //If the chain is restarted from a file with walkers + if (restart == 1) + { + //Only the main task opens and writes to the output file and prints to screen + if (ThisTask == 0) + { + //Open the walker file + sprintf(infname,"%s/walkers.pt.%d.out",All.OutputDir,All.Seed); + if(!(ifp=fopen(infname,"r"))) + { + sprintf(line, "Cannot open file `%s' for reading the walkers.", infname); + endrun(line); + } + + //Get first line of walker file + fgets(line,nline,ifp); + sscanf(line,"#Walkers/Parameters/seed/i %d %d %d %d\n", &nfilewalk, &nfilepar, &nrun, &istart); + + //Sanity Checks + if (nfilewalk != All.Nwalkers) + { + sprintf(line, "The walker file %s contains %d walkers (while the code needs %d)",infname,nfilewalk,All.Nwalkers); + endrun(line); + } + if (nfilepar != All.Nparam) + { + sprintf(line, "The walker file %s contains %d parameters (while the code needs %d)",infname,nfilepar,All.Nparam); + endrun(line); + } + if (nrun < 0) + { + sprintf(line, "The walker file %s has a negative run number (%02d)",infname,nrun); + endrun(line); + } + if (istart < 0) + { + sprintf(line, "The walker file %s has a negative starting step number (%02d)",infname,istart); + endrun(line); + } + + //Increment the Run Number + nrun++; + + //Increment the starting step + istart++; + + //Print what is done... + printf("%s\n%s Restarting parallel tempering method...\n",All.fullline,All.startline); + printf("%s Reading %d Walkers from %s...\n",All.startline,All.Nwalkers,infname); + + //Set output file and open file pointer + sprintf(outfname,"%s/pt%d.%03d.out",All.OutputDir,All.Seed,nrun); + ofp = fopen(outfname,"w"); + + //Get each line in the file + for (k = 0; k < All.Nwalkers; k++) + { + //Get the new line + fgets(line,nline,ifp); + tmp = strtok(line, " "); + //Get the probability + prob[k] = atof(tmp); + //Get beta + tmp = strtok(NULL, " "); + beta[k] = 1./atof(tmp); + //Get scale + tmp = strtok(NULL, " "); + scale[k] = atof(tmp); + //Get acceptance counters for each walker + tmp = strtok(NULL, " "); + ac[k] = atoi(tmp); + //Get all the parameters for the walker + for (i = 0; i < All.Nparam; i++) + { + tmp = strtok(NULL, " "); + x[k*All.Nparam+i] = atof(tmp); + } + } //End reading walker file + fclose(ifp); + } + + //Broadcast the nrun and istart from task 0 + MPI_Bcast(&nrun, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&istart, 1, MPI_INT, 0, MPI_COMM_WORLD); + + //Re-initialize random numbers + gsl_rng_set(rng_uniform_pt, All.Seed + 100 * nrun); + gsl_rng_set(rng_gaussian_pt, All.Seed + 100 * nrun); + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the scale array from task 0 + MPI_Bcast(scale, All.Nwalkers, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the scale array from task 0 + MPI_Bcast(beta, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + //Broadcast the acceptance counter array from task 0 + MPI_Bcast(ac, All.Nwalkers, MPI_INT, 0, MPI_COMM_WORLD); + } + + //Broadcast the probability array from task 0 + MPI_Bcast(prob, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + + //Define loop iteration variable + istep=istart; + isave=istart*All.Nwalkers; + doloop=1; + + //Inform everyone we are starting with the main MCMC loop + if (ThisTask == 0) printf("%s\n%s Starting main loop for parallel tempering at istart = %d...\n",All.fullline,All.startline,istart); + + //Start main MCMC loop, stop loop after some specified time is reached + while (doloop) + { + //Initialise Loop start time + lastlooptime = second() - All.starttime; + + //After every PT_STEPS_SIGMA_ADJUST chain step we adjust the scale for each walker + if (istep % PT_STEPS_SCALE_ADJUST == 0 && istep > 0) + { + for (k = 0; k < All.Nwalkers; k++) + { + facc = ((double)(ac[k]))/((double)(istep)); + //If the acceptance fraction is smaller than the target rate make the proposal step smaller + if ( facc < PT_TARGET_ACCEPTANCE) + scale[k] /= 1.0 + (PT_TARGET_ACCEPTANCE-facc) / PT_TARGET_ACCEPTANCE; + //If the acceptance fraction is higher than the target rate make the proposal step larger + if ( facc > PT_TARGET_ACCEPTANCE) + scale[k] *= 1.0 + (facc-PT_TARGET_ACCEPTANCE) / (1.0-PT_TARGET_ACCEPTANCE); + } + } + + //Loop over all walkers and check if they are swapped for another walker + for (k = 0; k < All.Nwalkers; k++) + { + //Randomly choose walker for swapping + do j = (int)(gsl_rng_uniform(rng_uniform_pt)*(double)(All.Nwalkers)); + while (j==k); + + // Check if the two walkers are swapped (positions and probability) + accept = ((beta[k]-beta[j]) * (prob[j]-prob[k]) > log(gsl_rng_uniform(rng_uniform_pt))); + + //Do not swap if the temperatures are equal + if (beta[k] == beta[j]) accept = 0; + + //If the swap is accepted + if (accept) + { + //Print the swapping to screen + if (ThisTask==0) printf("%s Swapping walker %d (T = %f, lnprob=%f) and %d (T = %f, lnprob=%f)\n",All.startline,k,1./beta[k],prob[k],j,1./beta[j],prob[j]); + //Swap walker k and walker j + for (i = 0; i < All.Nparam; i++) + { + //parameters + xswap = x[k*All.Nparam+i]; + x[k*All.Nparam+i] = x[j*All.Nparam+i]; + x[j*All.Nparam+i] = xswap; + } + //probability + px = prob[k]; + prob[k] = prob[j]; + prob[j] = px; + }//End accept + }//End loop over walkers + + //Print the header for the chain steps + if (ThisTask == 0) print_mcmc_header(); + + //Loop through the walkers + for (j = 0; j < All.Nwalkers/All.NUniverses; j++) + { + //Set the current time + All.timenow = second() - All.starttime; + + //Set up new trial walkers for all universes + for (itask = 0; itask < All.NUniverses; itask++) + { + //Current walker + iwalker = j*All.NUniverses + itask; + //Get this walkers previous probability + px = prob[iwalker]; + //Propose new walker + for (i = 0; i < All.Nparam; i++) + y[itask*All.Nparam+i] = x[iwalker*All.Nparam+i] + sigma[i] * scale[iwalker] * gsl_ran_gaussian(rng_gaussian_pt,1.0); + } + + //Broadcast the walker array y from task 0 + MPI_Bcast(y, All.NUniverses * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + //Compute the probability for each universe + py = lnprob(y); + MPI_Barrier(MPI_COMM_WORLD); + + //Go through all master tasks + for (itask = 0; itask < All.NUniverses; itask++) + { + //Set index of the current walker + iwalker = j*All.NUniverses + itask; + //Get this walkers previous probability + px = prob[iwalker]; + //If this is a master task but not task 0 + if (ThisTask == All.NTaskPerUniverse * itask && itask > 0) + { + //Send the probability to task 0 + MPI_Ssend(&py, 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + } + //If this is task 0 and it is not the first loop + if (ThisTask == 0 && itask > 0) + { + //Receive the probability from the other master task + MPI_Recv(&py, 1, MPI_FLOAT, All.NTaskPerUniverse * itask, TAG_PROB, MPI_COMM_WORLD, &status); + } + + //Sanity check if the computed probability is in a sensible range + if (!(py < 0 && py > -1.e10) ) py = -2.e10; + + //Check if new step is accepted + accept = (beta[iwalker] * (py-px) > log(gsl_rng_uniform(rng_uniform_pt))); + + //If the probability is beyond the edge do not accept + if (py < -1.e10) accept = 0; + + //If this is task 0 write all parameters and the probability to screen and output file + if (ThisTask == 0) + { + //If y is accepted set walker k to y and store probability and update accepted fraction + if (accept) + { + for (i = 0; i < All.Nparam; i++) x[iwalker*All.Nparam+i] = y[itask*All.Nparam+i]; + px = py; + prob[iwalker] = px; + ac[iwalker]++; + } + + //Print the walkers to screen + printf("%8d %8.1f",isave,All.timenow); + for (i = 0; i < All.Nparam; i++) printf(" %11.8f",x[iwalker*All.Nparam+i]); + printf(" %f %6.3f %6.3f\n",prob[iwalker],1./beta[iwalker],scale[iwalker]); + + //Output to file + fprintf(ofp,"%f",prob[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(ofp," %f",x[iwalker*All.Nparam+i]); + fprintf(ofp," %f %f %f %d %d %d\n",1./beta[iwalker],scale[iwalker],((double)(ac[iwalker]))/((double)(istep+1)),isave,istep,iwalker); + fflush(ofp); + + }//End task 0 + + //Update output index + isave++; + + }//loop through master tasks + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, All.Nwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + //Broadcast the probability array from task 0 + MPI_Bcast(prob, All.Nwalkers, MPI_FLOAT, 0, MPI_COMM_WORLD); + //Broadcast the acceptance counter array from task 0 + MPI_Bcast(ac, All.Nwalkers, MPI_INT, 0, MPI_COMM_WORLD); + + }//loop through walkers + + //Only task 0 writes the walker file + if (ThisTask == 0) + { + //Write all current walkers to a file + sprintf(outfname,"%s/walkers.pt.%d.out",All.OutputDir,All.Seed); + wfp = fopen(outfname,"w"); + fprintf(wfp,"#Walkers/Parameters/seed/i %d %d %d %d\n",All.Nwalkers,All.Nparam,nrun,istep); + for (iwalker = 0; iwalker < All.Nwalkers; iwalker++) + { + fprintf(wfp,"%f %f %f %d",prob[iwalker],1./beta[iwalker],scale[iwalker],ac[iwalker]); + for (i = 0; i < All.Nparam; i++) fprintf(wfp," %f",x[iwalker*All.Nparam+i]); + fprintf(wfp,"\n"); + } + fclose(wfp); + } + + //Update step + istep++; + + //Get time the initialisation loop took + lastlooptime = second() - All.starttime - lastlooptime; + All.timenow = second() - All.starttime; + + //End loop if projected finish time of next loop would be larger than the time limit + if (All.timenow+1.5*lastlooptime>All.timelimit) doloop = 0; + + }//loop + + + //Inform everyone we are done with the main MCMC loop + if (ThisTask == 0) printf("%s Done with parallel tempering method...\n",All.startline); + + //Free all arrays and the output file pointer + efree_movable(y); + efree_movable(x); + efree_movable(sigma); + efree_movable(scale); + efree_movable(beta); + efree_movable(prob); + efree_movable(ac); + if (ThisTask == 0) fclose(ofp); + gsl_rng_free(rng_gaussian_pt); + gsl_rng_free(rng_uniform_pt); + +} + + +/*! \brief This function prints a header line for the MCMC + * + * For each parameter the corresponding parameter name is printed with the correct indention. + */ +void print_mcmc_header(void) +{ + if (ThisTask > 0) return; + int i = 1; + printf("%s Step Time ",All.startline); + if (All.DeltaM0 >= 0.0) printf("(%02d)M0 ",i); + if (All.DeltaM0 >= 0.0) i++; + if (All.DeltaEpsilon0 >= 0.0) printf("(%02d)E0 ",i); + if (All.DeltaEpsilon0 >= 0.0) i++; + if (All.DeltaBeta0 >= 0.0) printf("(%02d)B0 ",i); + if (All.DeltaBeta0 >= 0.0) i++; +#ifndef SFE_SAME_SLOPE + if (All.DeltaGamma0 >= 0.0) printf("(%02d)G0 ",i); + if (All.DeltaGamma0 >= 0.0) i++; +#endif +#ifdef SFE_MPEAK_ZEVOLV + if (All.DeltaMZ >= 0.0) printf("(%02d)MZ ",i); + if (All.DeltaMZ >= 0.0) i++; +#endif +#ifdef SFE_NORM_ZEVOLV + if (All.DeltaEpsilonZ >= 0.0) printf("(%02d)EZ ",i); + if (All.DeltaEpsilonZ >= 0.0) i++; +#endif +#ifdef SFE_BETA_ZEVOLV + if (All.DeltaBetaZ >= 0.0) printf("(%02d)BZ ",i); + if (All.DeltaBetaZ >= 0.0) i++; +#endif +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + if (All.DeltaGammaZ >= 0.0) printf("(%02d)GZ ",i); + if (All.DeltaGammaZ >= 0.0) i++; +#endif +#endif + if (All.DeltaFesc >= 0.0) printf("(%02d)Fesc ",i); + if (All.DeltaFesc >= 0.0) i++; + if (All.DeltaFstrip >= 0.0) printf("(%02d)Fstrip ",i); + if (All.DeltaFstrip >= 0.0) i++; + if (All.DeltaTau0 >= 0.0) printf("(%02d)Tau0 ",i); + if (All.DeltaTau0 >= 0.0) i++; +#ifdef SAT_SFR_EXP_DECAY + if (All.DeltaTauD >= 0.0) printf("(%02d)TauD ",i); + if (All.DeltaTauD >= 0.0) i++; +#endif +#ifdef SAT_QUENCH_MASS_DEPENDENT + if (All.DeltaTauS >= 0.0) printf("(%02d)TauS ",i); + if (All.DeltaTauS >= 0.0) i++; +#endif + printf("(%02d)lnprob",i); + i++; + printf("\n"); +} + + +/*! \brief This function initialises a walker with random parameters within the specified range + * + * This function s initialises a walker p with parameters within a range that is specified in + * the parameter file. Within this range the parameters are uniformly sampled. + * + * \param *p Walker array that will be initialised + */ +void init_param(double *p) +{ + int i,k; + + //Initialise the uniform generator + gsl_rng *rng_uniform_init = gsl_rng_alloc(gsl_rng_ranlxd2); + gsl_rng_set(rng_uniform_init, All.Seed + 661894); + + for (k = 0; k < All.Nwalkers; k++) + { + i = 0; + + //M0 + if (All.DeltaM0 >= 0.0) p[k*All.Nparam + i] = (P.M0-All.DeltaM0/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaM0; + if (All.DeltaM0 >= 0.0) i++; + + //E0 + if (All.DeltaEpsilon0 >= 0.0) p[k*All.Nparam + i] = (log10(P.Epsilon0)-All.DeltaEpsilon0/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaEpsilon0; + if (All.DeltaEpsilon0 >= 0.0) i++; + + //B0 + if (All.DeltaBeta0 >= 0.0) p[k*All.Nparam + i] = (P.Beta0-All.DeltaBeta0/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaBeta0; + if (All.DeltaBeta0 >= 0.0) i++; + + //G0 +#ifndef SFE_SAME_SLOPE + if (All.DeltaGamma0 >= 0.0) p[k*All.Nparam + i] = (P.Gamma0-All.DeltaGamma0/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaGamma0; + if (All.DeltaGamma0 >= 0.0) i++; +#endif + + //MZ +#ifdef SFE_MPEAK_ZEVOLV + if (All.DeltaMZ >= 0.0) p[k*All.Nparam + i] = (P.MZ+P.M0-p[k*All.Nparam]-All.DeltaMZ/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaMZ; + if (All.DeltaMZ >= 0.0) i++; +#endif + + //EZ +#ifdef SFE_NORM_ZEVOLV + if (All.DeltaEpsilonZ >= 0.0) p[k*All.Nparam + i] = (P.EpsilonZ-All.DeltaEpsilonZ/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaEpsilonZ; + if (All.DeltaEpsilonZ >= 0.0) i++; +#endif + + //BZ +#ifdef SFE_BETA_ZEVOLV + if (All.DeltaBetaZ >= 0.0) p[k*All.Nparam + i] = (P.BetaZ+P.Beta0-p[k*All.Nparam+2]-All.DeltaBetaZ/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaBetaZ; + if (All.DeltaBetaZ >= 0.0) i++; +#endif + + //GZ +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + if (All.DeltaGammaZ >= 0.0) p[k*All.Nparam + i] = (P.GammaZ-All.DeltaGammaZ/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaGammaZ; + if (All.DeltaGammaZ >= 0.0) i++; +#endif +#endif + + //Fesc + if (All.DeltaFesc >= 0.0) p[k*All.Nparam + i] = (log10(P.Fesc)-All.DeltaFesc/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaFesc; + if (All.DeltaFesc >= 0.0) i++; + + //Fstrip + if (All.DeltaFstrip >= 0.0) p[k*All.Nparam + i] = (log10(P.Fstrip)-All.DeltaFstrip/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaFstrip; + if (All.DeltaFstrip >= 0.0) i++; + + //Tau0 + if (All.DeltaTau0 >= 0.0) p[k*All.Nparam + i] = (log10(P.Tau0)-All.DeltaTau0/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaTau0; + if (All.DeltaTau0 >= 0.0) i++; + + //TauD +#ifdef SAT_SFR_EXP_DECAY + if (All.DeltaTauD >= 0.0) p[k*All.Nparam + i] = (P.TauD-All.DeltaTauD/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaTauD; + if (All.DeltaTauD >= 0.0) i++; +#endif + + //TauS +#ifdef SAT_QUENCH_MASS_DEPENDENT + if (All.DeltaTauS >= 0.0) p[k*All.Nparam + i] = (P.TauS-All.DeltaTauS/2.0) + gsl_rng_uniform(rng_uniform_init)*All.DeltaTauS; + if (All.DeltaTauS >= 0.0) i++; +#endif + + } + + gsl_rng_free(rng_uniform_init); + +} + + +/*! \brief This function initialises the standard deviation with the specified range + * + * This function s initialises the standard deviation s with values as specified in + * the parameter file. The range is modified with the MCMC scale parameter. + * + * \param *s Standard deviation array that will be initialised + */ +void init_sigma(double *s) +{ + int i = 0; + + //M0 + if (All.DeltaM0 >= 0.0) s[i] = All.DeltaM0/pow(2.0,All.mcmca); + if (All.DeltaM0 >= 0.0) i++; + + //E0 + if (All.DeltaEpsilon0 >= 0.0) s[i] = All.DeltaEpsilon0/pow(2.0,All.mcmca); + if (All.DeltaEpsilon0 >= 0.0) i++; + + //B0 + if (All.DeltaBeta0 >= 0.0) s[i] = All.DeltaBeta0/pow(2.0,All.mcmca); + if (All.DeltaBeta0 >= 0.0) i++; + + //G0 +#ifndef SFE_SAME_SLOPE + if (All.DeltaGamma0 >= 0.0) s[i] = All.DeltaGamma0/pow(2.0,All.mcmca); + if (All.DeltaGamma0 >= 0.0) i++; +#endif + + //MZ +#ifdef SFE_MPEAK_ZEVOLV + if (All.DeltaMZ >= 0.0) s[i] = All.DeltaMZ/pow(2.0,All.mcmca); + if (All.DeltaMZ >= 0.0) i++; +#endif + + //EZ +#ifdef SFE_NORM_ZEVOLV + if (All.DeltaEpsilonZ >= 0.0) s[i] = All.DeltaEpsilonZ/pow(2.0,All.mcmca); + if (All.DeltaEpsilonZ >= 0.0) i++; +#endif + + //BZ +#ifdef SFE_BETA_ZEVOLV + if (All.DeltaBetaZ >= 0.0) s[i] = All.DeltaBetaZ/pow(2.0,All.mcmca); + if (All.DeltaBetaZ >= 0.0) i++; +#endif + + //GZ +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + if (All.DeltaGammaZ >= 0.0) s[i] = All.DeltaGammaZ/pow(2.0,All.mcmca); + if (All.DeltaGammaZ >= 0.0) i++; +#endif +#endif + + //Fesc + if (All.DeltaFesc >= 0.0) s[i] = All.DeltaFesc/pow(2.0,All.mcmca); + if (All.DeltaFesc >= 0.0) i++; + + //Fstrip + if (All.DeltaFstrip >= 0.0) s[i] = All.DeltaFstrip/pow(2.0,All.mcmca); + if (All.DeltaFstrip >= 0.0) i++; + + //Tau0 + if (All.DeltaTau0 >= 0.0) s[i] = All.DeltaTau0/pow(2.0,All.mcmca); + if (All.DeltaTau0 >= 0.0) i++; + + //TauD +#ifdef SAT_SFR_EXP_DECAY + if (All.DeltaTauD >= 0.0) s[i] = All.DeltaTauD/pow(2.0,All.mcmca); + if (All.DeltaTauD >= 0.0) i++; +#endif + + //TauS +#ifdef SAT_QUENCH_MASS_DEPENDENT + if (All.DeltaTauS >= 0.0) s[i] = All.DeltaTauS/pow(2.0,All.mcmca); + if (All.DeltaTauS >= 0.0) i++; +#endif + +} + + +/*! \brief This function computes the log probability for the walkers on each master task + * + * This function first sets the values of the local parameter structure P to the specified values + * in the array p that consists of NUniverse walkers (one for each universe that is computed in parallel). + * Then a few basic physical boundary conditions are check and if not met a very large value is returned. + * Otherwise the galaxies are assembled for each universe and \f$\chi^2\f$ for each universe is computed. + * Each master task returns its respective \f$-\chi^2/2\f$ (all other tasks return 0). + * + * \param *p Array with NUniverse walkers that will be used to compute the probability + */ +float lnprob(double *p) +{ + + int i = 0; + int thisUniverse = MasterTask / All.NTaskPerUniverse; + float chi2 = 0.0; + + // Initialize the total DoF + DoF = 0; + DoFsmf = 0; + DoFfq = 0; + DoFcsfrd = 0; + DoFssfr = 0; + DoFwp = 0; + + // Update all the parameters for each task / universe + + //M0 + if (All.DeltaM0 >= 0.0) P.M0 = p[i + All.Nparam * thisUniverse]; + if (All.DeltaM0 >= 0.0) i++; + + //E0 + if (All.DeltaEpsilon0 >= 0.0) P.Epsilon0 = pow(10.,p[i + All.Nparam * thisUniverse]); + if (All.DeltaEpsilon0 >= 0.0) i++; + + //B0 + if (All.DeltaBeta0 >= 0.0) P.Beta0 = p[i + All.Nparam * thisUniverse]; + if (All.DeltaBeta0 >= 0.0) i++; + + //G0 +#ifndef SFE_SAME_SLOPE + if (All.DeltaGamma0 >= 0.0) P.Gamma0 = p[i + All.Nparam * thisUniverse]; + if (All.DeltaGamma0 >= 0.0) i++; +#else + P.Gamma0 = P.Beta0; +#endif + + //MZ +#ifdef SFE_MPEAK_ZEVOLV + if (All.DeltaMZ >= 0.0) P.MZ = p[i + All.Nparam * thisUniverse]; + if (All.DeltaMZ >= 0.0) i++; +#else + P.MZ = 0.0; +#endif + + //EZ +#ifdef SFE_NORM_ZEVOLV + if (All.DeltaEpsilonZ >= 0.0) P.EpsilonZ = p[i + All.Nparam * thisUniverse]; + if (All.DeltaEpsilonZ >= 0.0) i++; +#else + P.EpsilonZ = 0.0; +#endif + + //BZ +#ifdef SFE_BETA_ZEVOLV + if (All.DeltaBetaZ >= 0.0) P.BetaZ = p[i + All.Nparam * thisUniverse]; + if (All.DeltaBetaZ >= 0.0) i++; +#else + P.BetaZ = 0.0; +#endif + + //GZ +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + if (All.DeltaGamma0 >= 0.0) P.GammaZ = p[i + All.Nparam * thisUniverse]; + if (All.DeltaGamma0 >= 0.0) i++; +#else + P.GammaZ = P.BetaZ; +#endif +#else + P.GammaZ = 0.0; +#endif + + //Fesc + if (All.DeltaFesc >= 0.0) P.Fesc = pow(10.,p[i + All.Nparam * thisUniverse]); + if (All.DeltaFesc >= 0.0) i++; + + //Fstrip + if (All.DeltaFstrip >= 0.0) P.Fstrip = pow(10.,p[i + All.Nparam * thisUniverse]); + if (All.DeltaFstrip >= 0.0) i++; + + //Tau0 + if (All.DeltaTau0 >= 0.0) P.Tau0 = pow(10.,p[i + All.Nparam * thisUniverse]); + if (All.DeltaTau0 >= 0.0) i++; + + //TauD +#ifdef SAT_SFR_EXP_DECAY + if (All.DeltaTauD >= 0.0) P.TauD = p[i + All.Nparam * thisUniverse]; + if (All.DeltaTauD >= 0.0) i++; +#else + P.TauD = 0.0; +#endif + + //TauS +#ifdef SAT_QUENCH_MASS_DEPENDENT + if (All.DeltaTauS >= 0.0) P.TauS = p[i + All.Nparam * thisUniverse]; + if (All.DeltaTauS >= 0.0) i++; +#else + P.TauS = 0.0; +#endif + + //Physical boundary conditions + if (P.Fesc > 1.0) return -1.e10; +#ifndef SAT_STRIP_USE_MSTAR + if (P.Fstrip > 1.0) return -1.e10; +#endif + if (P.Epsilon0+P.EpsilonZ <= 0.0) return -1.e10; + if (P.M0+P.MZ <= 0.0) return -1.e10; + + //Populate haloes with galaxies + make_galaxies(); + + //Get statistics + get_statistics(); + + //Calculate Chi^2 for this master task +#ifdef READ_SMF + chi2 += chi2_smf(); +#endif + +#ifdef READ_FQ + chi2 += chi2_fq(); +#endif + +#ifdef READ_CSFRD + chi2 += chi2_csfrd(); +#endif + +#ifdef READ_SSFR + chi2 += chi2_ssfr(); +#endif + +#ifdef READ_WP + chi2 += chi2_wp(); +#endif + + return -chi2/2.0; + +} + + +/*! \brief This function gets the total \f$\chi^2\f$ and prints it to screen. + * + * This function gets the contribution to \f$\chi^2\f$ from each observable. All contributions and the total are + * printed to screen. + */ +float print_chi2(void) +{ + int i; + float *chi2; + + //Allocate the chi2 array for All.Nobs values plus the total value + chi2 = emalloc("CHI2", (All.Nobs + 1) * sizeof(float)); + //Compute Chi2 + get_chi2(chi2); + //Init + i = 0; + + //Only the main task gets the chi^2 and prints to screen + if (ThisTask == 0) + { + //Print to screen + printf("%s\n",All.fullline); + + //Print each contribution to the total chi^2 +#ifdef READ_SMF + printf("%s Chi2 from SMF = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoFsmf); + i++; +#endif + +#ifdef READ_FQ + printf("%s Chi2 from Quenched Fractions = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoFfq); + i++; +#endif + +#ifdef READ_CSFRD + printf("%s Chi2 from Cosmic SFR Density = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoFcsfrd); + i++; +#endif + +#ifdef READ_SSFR + printf("%s Chi2 from Specific SFRs = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoFssfr); + i++; +#endif + +#ifdef READ_WP + printf("%s Chi2 from Clustering = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoFwp); + i++; +#endif + + //Print total chi^2 + printf("%s Chi2 (Total) = %12.5f -> Chi2/DoF = %12.5f\n",All.startline,chi2[i],chi2[i]/DoF); + + } + + //Free the chi2 array + efree(chi2); + + return chi2[i]; + +} + + +/*! \brief This function computes the total \f$\chi^2\f$ and returns it + * + * This function initialises the total \f$\chi^2\f$ and the total and partial degrees of freedom to zero + * and computes the contribution to \f$\chi^2\f$ from each observable. All contributions and the total are + * returned as a pointer. + */ +void get_chi2(float *chi2) +{ + int i; + float chi2now,chi2tot; + + //Initialise everything to zero + chi2tot = 0.0; + DoF = 0; + DoFsmf = 0; + DoFfq = 0; + DoFcsfrd = 0; + DoFssfr = 0; + DoFwp = 0; + i = 0; + + //Only the master task computes chi^2 + if (ThisTask == MasterTask) + { +#ifdef READ_SMF + chi2now = chi2_smf(); + chi2tot += chi2now; + chi2[i] = chi2now; + i++; +#endif + +#ifdef READ_FQ + chi2now = chi2_fq(); + chi2tot += chi2now; + chi2[i] = chi2now; + i++; +#endif + +#ifdef READ_CSFRD + chi2now = chi2_csfrd(); + chi2tot += chi2now; + chi2[i] = chi2now; + i++; +#endif + +#ifdef READ_SSFR + chi2now = chi2_ssfr(); + chi2tot += chi2now; + chi2[i] = chi2now; + i++; +#endif + +#ifdef READ_WP + chi2now = chi2_wp(); + chi2tot += chi2now; + chi2[i] = chi2now; + i++; +#endif + + //Get total chi^2 + chi2[i] = chi2tot; + + } + +} + + +/*! \brief This function computes the contribution to chi^2 from the SMF + * + * This function loops over all data points of the SMFs in the interpolation range and sums + * their contribution. The total squared error used is the sum of the squared error of the observations + * and the model (Poisson). Each data point is weighted by its nfit value. The total and partial + * number of degrees of freedom is incremented as well. + * The returned \f$\chi^2\f$ is given by \f[\chi^2 = \sum_i n_\mathrm{fit,i} + * \frac{(\Phi_\mathrm{obs,i}-\Phi_\mathrm{mod,i})^2}{\sigma_\mathrm{obs,i}^2+\sigma_\mathrm{mod,i}^2} .\f] + */ +float chi2_smf(void) +{ + int i; + float mod, obs, sigmamod, sigmaobs; + float chi2 = 0.0; + + //If this is not a master task return 0 + if (ThisTask != MasterTask) return 0.0; + + //Go through all data points of the SMF + for (i = 0; i < All.Nsmf; i++) + { + //If the data point is within the interpolation range add it to the chi^2 + if (Smf[i].mod_y > 0.0 && Smf[i].nfit > 0) + { + obs = Smf[i].obs_y; + mod = log10(Smf[i].mod_y); + sigmaobs = Smf[i].obs_sigma; + sigmamod = log10(Smf[i].mod_sigma); + chi2 += (obs-mod) * (obs-mod) / (sigmaobs*sigmaobs + sigmamod*sigmamod) * (float)(Smf[i].nfit); + DoF += Smf[i].nfit; + DoFsmf += Smf[i].nfit; + } + } + + return chi2; +} + + +/*! \brief This function computes the contribution to \f$\chi^2\f$ from the quenched fractions + * + * This function loops over all data points of the quenched fractions in the interpolation range and sums + * their contribution. The total squared error used is the sum of the squared error of the observations + * and the model (Poisson). Each data point is weighted by its nfit value. The total and partial + * number of degrees of freedom is incremented as well. + * The returned \f$\chi^2\f$ is given by \f[\chi^2 = \sum_i n_\mathrm{fit,i} + * \frac{(f_\mathrm{q,obs,i}-f_\mathrm{q,mod,i})^2}{\sigma_\mathrm{obs,i}^2+\sigma_\mathrm{mod,i}^2} .\f] + */ +float chi2_fq(void) +{ + int i; + float mod, obs, sigmamod, sigmaobs; + float chi2 = 0.0; + + //If this is not a master task return 0 + if (ThisTask != MasterTask) return 0.0; + + //Go through all data points of the FQs + for (i = 0; i < All.Nfq; i++) + { + //If the data point is within the interpolation range add it to the chi^2 + if (Fq[i].mod_y >= 0.0 && Fq[i].nfit > 0) + { + obs = Fq[i].obs_y; + mod = Fq[i].mod_y; + sigmaobs = Fq[i].obs_sigma; + sigmamod = Fq[i].mod_sigma; + chi2 += (obs-mod) * (obs-mod) / (sigmaobs*sigmaobs + sigmamod*sigmamod) * (float)(Fq[i].nfit); + DoF += Fq[i].nfit; + DoFfq += Fq[i].nfit; + } + } + + return chi2; +} + + +/*! \brief This function computes the contribution to chi^2 from the cosmic SFR density + * + * This function loops over all data points of the CSFRD in the interpolation range and sums + * their contribution. The total error used is only the observational error. Each data point + * is weighted by its nfit value. The total and partial number of degrees of freedom is + * incremented as well. + * The returned \f$\chi^2\f$ is given by \f[\chi^2 = \sum_i n_\mathrm{fit,i} + * \frac{(\dot\rho^*_\mathrm{obs,i}-\dot\rho^*_\mathrm{mod,i})^2}{\sigma_\mathrm{obs,i}^2+\sigma_\mathrm{mod,i}^2} .\f] + */ +float chi2_csfrd(void) +{ + int i; + float mod, obs, sigmaobs; + float chi2 = 0.0; + + //If this is not a master task return 0 + if (ThisTask != MasterTask) return 0.0; + + //Go through all data points of the CSFRD + for (i = 0; i < All.Ncsfrd; i++) + { + //If the data point is within the interpolation range add it to the chi^2 + if (Csfrd[i].mod_y > 0 && Csfrd[i].nfit > 0) + { + obs = Csfrd[i].obs_y; + mod = log10(Csfrd[i].mod_y); + sigmaobs = Csfrd[i].obs_sigma; + chi2 += (obs-mod) * (obs-mod) / (sigmaobs*sigmaobs) * (float)(Csfrd[i].nfit); + DoF += Csfrd[i].nfit; + DoFcsfrd += Csfrd[i].nfit; + } + } + + return chi2; +} + + +/*! \brief This function computes the contribution to chi^2 from the specific SFRs + * + * This function loops over all data points of the SSFRs in the interpolation range and sums + * their contribution. The total squared error used is the sum of the squared error of the observations + * and the model (Poisson). Each data point is weighted by its nfit value. The total and partial + * number of degrees of freedom is incremented as well. + * The returned \f$\chi^2\f$ is given by \f[\chi^2 = \sum_i n_\mathrm{fit,i} + * \frac{(\Psi_\mathrm{obs,i}-\Psi_\mathrm{mod,i})^2}{\sigma_\mathrm{obs,i}^2+\sigma_\mathrm{mod,i}^2} .\f] + */ +float chi2_ssfr(void) +{ + int i; + float mod, obs, sigmamod, sigmaobs; + float chi2 = 0.0; + + //If this is not a master task return 0 + if (ThisTask != MasterTask) return 0.0; + + //Go through all data points of the SSFR + for (i = 0; i < All.Nssfr; i++) + { + //If the data point is within the interpolation range add it to the chi^2 + if (Ssfr[i].mod_y > 0.0 && Ssfr[i].nfit > 0) + { + obs = Ssfr[i].obs_y; + mod = log10(Ssfr[i].mod_y / All.t_unit); + sigmaobs = Ssfr[i].obs_sigma; + sigmamod = log10(Ssfr[i].mod_sigma); + chi2 += (obs-mod) * (obs-mod) / (sigmaobs*sigmaobs + sigmamod*sigmamod) * (float)(Ssfr[i].nfit); + DoF += Ssfr[i].nfit; + DoFssfr += Ssfr[i].nfit; + } + } + + return chi2; +} + + +/*! \brief This function computes the contribution to chi^2 from the projected correlation functions + * + * This function loops over all data points of the projected correltion functions up to the maximum distance + * given by #WP_RMAX times the box length and sums their contribution. The total squared error used is the + * sum of the squared error of the observations and the model (Poisson). Each data point is weighted by its + * nfit value. The total and partial number of degrees of freedom is incremented as well. + * The returned \f$\chi^2\f$ is given by \f[\chi^2 = \sum_i n_\mathrm{fit,i} + * \frac{(w_\mathrm{p,obs,i}-w_\mathrm{p,mod,i})^2}{\sigma_\mathrm{obs,i}^2+\sigma_\mathrm{mod,i}^2} .\f] + */ +float chi2_wp(void) +{ + int i; + float mod, obs, sigmamod, sigmaobs; + float chi2 = 0.0; + + //If this is not a master task return 0 + if (ThisTask != MasterTask) return 0.0; + + //Go through all data points of the WP + for (i = 0; i < All.Nwp; i++) + { + //If the data point is within the interpolation range add it to the chi^2 + if (Wp[i].mod_y > 0.0 && Wp[i].nfit > 0 && Wp[i].obs_x < WP_RMAX * All.Lbox) + { + obs = Wp[i].obs_y; + mod = Wp[i].mod_y; + sigmaobs = Wp[i].obs_sigma; + sigmamod = Wp[i].mod_sigma; + chi2 += (obs-mod) * (obs-mod) / (sigmaobs*sigmaobs + sigmamod*sigmamod) * (float)(Wp[i].nfit); + DoF += Wp[i].nfit; + DoFwp += Wp[i].nfit; + } + } + + return chi2; +} diff --git a/src/functions.c b/src/functions.c new file mode 100644 index 0000000..4d6c194 --- /dev/null +++ b/src/functions.c @@ -0,0 +1,330 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File functions.c // +// Contains general functions // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file functions.c +/// \brief Contains general functions +/// +/// This file contains general functions that are used to compute various quantities. +/// There are several functions that compare two values and can be used in the qsort algorithm. +/// The file also contains functions to search numbers in a sorted list. +/// Also included are cosmology functions to calculate the cosmic time. +/// The random number generator or table is accessed through a function in this file. +/// A function to compute the current CPU time is included as well. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/*! \brief This function compares two floating point numbers + * + * \param a First floating point number + * \param b Second floating point number + */ +int compare_float(const void *a, const void *b) +{ + float c = *(const float*) a; + float d = *(const float*) b; + return (c > d) - (c < d); +} + +/*! \brief This function compares the IDs of two search_list_IDs structures + * + * \param a First search_list_IDs structure + * \param b Second search_list_IDs structure + */ +int compare_id(const void *a, const void *b) +{ + const struct search_list_IDs *c = a; + const struct search_list_IDs *d = b; + return (c->ID > d->ID) - (c->ID < d->ID); +} + + +/*! \brief This function compares the loads of two task_load structures + * + * \param a First task_load structure + * \param b Second task_load structure + */ +int compare_load(const void *a, const void *b) +{ + const struct task_list *c = a; + const struct task_list *d = b; + return (c->LoadInTask < d->LoadInTask) - (c->LoadInTask > d->LoadInTask); +} + + +/*! \brief This function compares the scale factors of two haloes + * + * If they are the same compare the haloid of the haloes. + * This is done to make the sorting stable. + * + * \param a First halo structure + * \param b Second halo structure + */ +int compare_scale(const void *a, const void *b) +{ + const struct halo *c = a; + const struct halo *d = b; + int scale = (c->a > d->a) - (c->a < d->a); + return scale != 0 ? scale : (d->haloid > c->haloid) - (d->haloid < c->haloid); +} + + +/*! \brief This function compares the imain of two haloes + * + * This is done to sort the haloes by the task they need to go. + * If they are the same compare the iscale of the haloes which gives the original index. + * This is done to make the sorting stable. + * + * \param a First halo structure + * \param b Second halo structure + */ +int compare_totask(const void *a, const void *b) +{ + const struct halo *c = a; + const struct halo *d = b; + int totask = c->imain - d->imain; + return totask != 0 ? totask : (c->iscale > d->iscale) - (c->iscale < d->iscale); +} + + +/*! \brief This function compares the forestid of two haloes + * + * This is done to sort the haloes by the forest ID + * If they are the same compare the iscale of the haloes which gives the original index. + * This is done to make the sorting stable. + * + * \param a First halo structure + * \param b Second halo structure + */ +#ifdef COMPUTE_ICM +int compare_forest(const void *a, const void *b) +{ + const struct halo *c = a; + const struct halo *d = b; + int forest = (c->forestid > d->forestid) - (c->forestid < d->forestid); + return forest != 0 ? forest : (c->iscale > d->iscale) - (c->iscale < d->iscale); +} +#endif + + +/*! \brief This function compares the tree or forest length of two haloes + * + * This is done to sort the haloes by their tree/forest length. + * First compare the idescgal of the haloes which gives the tree or forest length. + * If they are the same compare the icoprog of the haloes which gives the original index. + * This is done to make the sorting stable. + * + * \param a First halo structure + * \param b Second halo structure + */ +int compare_size(const void *a, const void *b) +{ + const struct halo *c = a; + const struct halo *d = b; + int size = (c->idescgal < d->idescgal) - (c->idescgal > d->idescgal); + return size != 0 ? size : (c->icoprog > d->icoprog) - (c->icoprog < d->icoprog); +} + + +/*! \brief This function searches an array of search_list_IDs structures + * + * The search_list_IDs structure array a with length n is searched for the ID key. + * If it is found the position of the key in the unsorted list is returned. + * If it is found a value of -99 is returned. + * + * \param a search_list_IDs structure that will be scanned + * \param n Size of the search_list_IDs structure array + * \param key ID that is searched + */ +int binary_search_id(struct search_list_IDs *a, int n, IDType key) +{ + int imin = 0; + int imax = n-1; + int imid = (imin+imax)/2; + // continually narrow search until just one element remains + while (imin <= imax) + { + // determine which subarray to search + if (a[imid].ID < key) + // change min index to search upper subarray + imin = imid + 1; + else if ((imin==imax)&&(a[imid].ID!=key)) + return -99; + else if (a[imid].ID == key) + // key found at index imid + return a[imid].position; + else + // change max index to search lower subarray + imax = imid - 1; + //calculate the midpoint for roughly equal partition + imid = (imin+imax)/2; + } + // key not found + return -99; +} + + +/*! \brief This function searches an array of floats + * + * The float array a with length n is searched for the key. + * If it is found the index of the key is returned. + * If it is found a value of -99 is returned. + * + * \param a float array that will be scanned + * \param n Size of the float array + * \param key that is searched + */ +int binary_search_float(float *a, int n, float key) +{ + int imin = 0; + int imax = n-1; + int imid = (imin+imax)/2; + // continually narrow search until just one element remains + while (imin <= imax) + { + // determine which subarray to search + if (a[imid] < key) + // change min index to search upper subarray + imin = imid + 1; + else if ((imin==imax)&&(a[imid]!=key)) + return -99; + else if (a[imid] == key) + // key found at index imid + return imid; + else + // change max index to search lower subarray + imax = imid - 1; + //calculate the midpoint for roughly equal partition + imid = (imin+imax)/2; + } + // key not found + return -99; +} + + +/*! \brief This function computes the cosmic time as a function of the scale factor + * + * The age of the universe at any scale factor (cosmic time) is computed with the approximation + * given in Carroll, Press & Turner (1992), + * equations 17/18. + * + * \param a The scale factor at which the cosmic time is computed + */ +float cosmictime(float a) +{ + float z,omega_L,f,absf,arg,t; + z = 1.0/a-1.0; + if (z < 0.0) return -1; + if (All.Omega_0+All.Omega_Lambda_0==1.0) + { + omega_L = a*a*a*All.Omega_Lambda_0/(a+All.Omega_0*(1.0-a)+All.Omega_Lambda_0*(a*a*a-a)); + f = 0.7*Omega(z) - 0.3*omega_L + 0.3; + if (1.0-f < 0) absf = f-1.0; + else absf = 1.0-f; + arg = sqrt(absf/f); + if (f < 1) t = (2.0*All.Hubbletime/(3.0*Epeebles(z)*sqrt(absf)))*asinh(arg); + else t = (2.0*All.Hubbletime/(3.0*Epeebles(z)*sqrt(absf)))*asin(arg); + return t/All.t_unit; + } + else if (All.Omega_0+All.Omega_Lambda_0<1.0) + { + return All.Hubbletime*(sqrt(1+All.Omega_0*z)/((1-All.Omega_0)*(1+z)) - (All.Omega_0/(2.*pow(1-All.Omega_0, 1.5))) * acosh(1+2.*(1-All.Omega_0)/(All.Omega_0*(1+z))))/All.t_unit; + } + else return -1; +} + + +/*! \brief This function computes the matter density at a given redshift + * + * \param z The redshift at which the matter density is computed + */ +float Omega(float z) +{ + //omega as a function of redshift + if (z < 0.005) + return All.Omega_0; + else if (All.Omega_0+All.Omega_Lambda_0==1.0) + return All.Omega_0*pow(1+z, 3.0)/(1-All.Omega_0 + pow(1+z, 3.0)*All.Omega_0); + else if (All.Omega_0+All.Omega_Lambda_0<1.0) + return All.Omega_0*(1+z)/(1 + All.Omega_0*z); + return 0.0; +} + + +/*! \brief This function computes the peebles function \f$E(z) = H(z)/\mathrm{H}_0\f$ at a given redshift + * ( Peebles 2010, p. 312) + * + * \param z The redshift at which the matter density is computed + */ +float Epeebles(float z) +{ + float omega_R, omega_Lambda; + omega_R = 0.0; + omega_Lambda = 0.0; + if (All.Omega_0+All.Omega_Lambda_0==1.0) omega_Lambda = 1.0-All.Omega_0; + else if (All.Omega_0+All.Omega_Lambda_0<1.0) omega_R = 1.0-All.Omega_0; + return sqrt(All.Omega_0*(1.0+z)*(1.0+z)*(1.0+z)+omega_R*(1.0+z)*(1.0+z)+omega_Lambda); +} + + +/*! \brief This function computes the dynamical time using the virial overdensity by + * Bryan & Norman (1998) + * + * \param a The scale factor at which the cosmic time is computed + */ +float tdyn(float a) +{ + float x,virdens,z; + z = 1.0/a-1.0; + x = (Omega(z)-1.0); + virdens = 18.0*M_PI*M_PI+82.0*x-39.0*x*x; + return All.Hubbletime/All.t_unit/sqrt(virdens/2.0)/Epeebles(z); +} + + +/*! \brief This function returns a gaussian random number with sigma = 1.0 + * + * \param index The index of the array that will get a random number + */ +float get_gaussian_random_number(int index) +{ +#if (RANDOM_NUMBER_TABLE > 0) + return RndTableGaussian[(index % RANDOM_NUMBER_TABLE)]; +#else + return gsl_ran_gaussian(rng_gaussian,1.0); +#endif +} + + +/*! \brief This function returns a uniform random number between 0 and 1 + * + * \param index The index of the array that will get a random number + */ +float get_uniform_random_number(int index) +{ +#if (RANDOM_NUMBER_TABLE > 0) + return RndTableUniform[(index % RANDOM_NUMBER_TABLE)]; +#else + return gsl_rng_uniform(rng_uniform); +#endif +} + + +/*! \brief This function returns the time in seconds + */ +double second(void) +{ + return MPI_Wtime(); +} diff --git a/src/galaxies.c b/src/galaxies.c new file mode 100644 index 0000000..7f2e83b --- /dev/null +++ b/src/galaxies.c @@ -0,0 +1,730 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File galaxies.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file galaxies.c +/// \brief Contains functions that populate haloes in a merger tree with galaxies +/// +/// This file contains all functions that are needed to compute galaxy related quantities. +/// This includes a function to initialise all arrays, a function that generates all galaxies +/// by calling a function that resets all galaxy quantities followed by calling a function that +/// assembles all galaxies, and a function that finalises the galaxy computation by freeing all +/// memory. Also this file contains some functions that are needed for the galaxy computation, +/// such as functions that compute the current stellar mass and ICM by summing over the stellar +/// populations formed at each scale factor, and a function to compute the dynamical friction time. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + +#ifdef HDF5_SUPPORT +#include +#endif + + +/*! \brief This function allocates and initialises all arrays needed for galaxy formation + * + * This function should be called once before the make_galaxies is called to set up all arrays + * that are needed. This includes the array #MassFormed that stores the stellar populations in the galaxy + * that formed in each branch and the array #ICMFormed that stores the same for the ICM. Also the imass + * index is set which links each halo to a mass array. + */ +void init_galaxies(void) +{ + int ihalo,imass; + +#ifndef COMPUTE_ICM + int itree; +#else + int iforest; +#endif + + //First find the maximum number of leaves +#ifndef COMPUTE_ICM + //Loop through all trees + for (itree = 0; itree < Ntrees; itree++) + { + //Go through all haloes in this tree + for (ihalo = OffsetHalos[itree], imass = 0; ihalo < OffsetHalos[itree] + NhalosInTree[itree]; ihalo++) + { +#else + //Loop through all forests + for (iforest = 0; iforest < Nforests; iforest++) + { + //Go through all haloes in this tree + for (ihalo = OffsetHalos[iforest], imass = 0; ihalo < OffsetHalos[iforest] + NhalosInForest[iforest]; ihalo++) + { +#endif + //If this halo does not have any progenitors + if (H[ihalo].np < 1) + { + //Set the branch number for this halo (equal to the number of its leaf) + H[ihalo].imass = imass; + //Increment the number of leaves + imass++; + } + //If the halo has a descendant set its imass to this halo's imass + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].imass = H[ihalo].imass; + } + //If imass in this tree/forest is larger than the global All.MaxNLeaves increase the global one + if (imass > All.MaxNLeaves) All.MaxNLeaves = imass; + } + + //For formating purposes add the two braces +#ifdef NEVERDEF +} +} +#endif + + //Get the maximum number of haloes per scale factor in any tree throughout all tasks + MPI_Allreduce(MPI_IN_PLACE, &All.MaxNLeaves, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + + //Allocate the array MassFormed which stores the mass of all populations in a branch + MassFormed = emalloc_movable(&MassFormed,"MassFormed", All.MaxNLeaves * All.NTimesteps * NThread * sizeof(float)); +#ifdef COMPUTE_ICM + ICMFormed = emalloc_movable(&ICMFormed, "ICMFormed", All.MaxNLeaves * All.NTimesteps * NThread * sizeof(float)); +#endif + + //Allocate arrays for statistics + Modelsmf = emalloc_movable(&Modelsmf, "ModelSMF", All.NTimesteps * All.Nmstar * NThread * sizeof(float)); + Modelsmfred = emalloc_movable(&Modelsmfred, "ModelSMFRed", All.NTimesteps * All.Nmstar * NThread * sizeof(float)); + Modelcsfrd = emalloc_movable(&Modelcsfrd, "ModelCSFRD", All.NTimesteps * NThread * sizeof(float)); + Modelssfr = emalloc_movable(&Modelssfr, "ModelSSFR", All.NTimesteps * All.Nmstar * NThread * sizeof(float)); + +#ifdef READ_WP + //Allocate arrays for the correlation functions + Radius = emalloc_movable(&Radius, "RADIUS", All.Nwpset * WP_RBINS_INT * sizeof(float)); + Modelxi = emalloc_movable(&Modelxi, "MODELXI", All.Nwpset * WP_RBINS_INT * sizeof(float)); + Modelxierr = emalloc_movable(&Modelxierr, "MODELXIERR", All.Nwpset * WP_RBINS_INT * sizeof(float)); +#endif + + //Set number of entries per thread for MassFormed + All.NMassFormed = All.MaxNLeaves * All.NTimesteps; + //Set number of entries per thread for Statistics + All.NStatistics = All.Nmstar * All.NTimesteps; + + //Print the memory usage + HighMark = 0; + report_memory_usage(&HighMark, "Init_Galaxies"); + +} + + +/*! \brief This function frees all arrays needed for galaxy formation + * + * This function should be called once after make_galaxies has been called (as often as needed) to free + * all arrays that were needed. + */ +void finish_galaxies(void) +{ +#ifdef READ_WP + efree(Modelxierr); + efree(Modelxi); + efree(Radius); +#endif + efree(Modelssfr); + efree(Modelcsfrd); + efree(Modelsmfred); + efree(Modelsmf); +#ifdef COMPUTE_ICM + efree_movable(ICMFormed); +#endif + efree_movable(MassFormed); + + //And print the memory usage + report_memory_usage(&HighMark, "Finish_Galaxies"); + +} + + +/*! \brief This function creates galaxies in the dark matter halo trees + * + * This function should be called each time the haloes shall be populated with galaxies. It needs to have + * #init_galaxies be called once at some point before. + */ +void make_galaxies(void) +{ + //Print what is done... + if (ThisTask == 0 && All.verbose >= VERBOSE_ALL) + printf("%s\n%s Populating trees with galaxies...\n", All.fullline,All.startline); + + reset_galaxies(); + assemble_galaxies(); + + //Print that we are done here... + if (ThisTask == 0 && All.verbose >= VERBOSE_ALL) + printf("%s ... done populating the trees with galaxies.\n",All.startline); + +} + + +/*! \brief This function resets all galaxy related variables + * + * This function should be called before each call of #assemble_galaxies. It resets all variables to + * the default values without containing any stars. All mmp are set to 1 and the coprog indices are set + * to -1 so that there are no mergers (yet). Also all haloes are potentially still present so gone is set + * to zero. All SFRs and stellar masses are set to zero and the dynamical friction time is set to the current + * time. All orphan virial masses are set to the mass at the time they became an orphan. + * The mass arrays that hold the stellar masses formed in each branch are set to zero. Finally the seed for + * the gaussian random number generator is reset if the random number table is not used. + */ +void reset_galaxies(void) +{ + int i; + + //Reset all galaxy properties in the haloes to zero and the dynamical friction time to the current cosmic time +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for schedule(static) +#endif + for (i = 0; i < Nhalos; i++) + { + H[i].mmp = 1; + H[i].icoprog = -1; + H[i].itdf = -1; + H[i].idescgal = H[i].idesc; + H[i].gone = 0; + + if (H[i].np > 1) H[i].np = 1; + + H[i].sfr = 0.0; + H[i].mstar = 0.0; + H[i].icm = 0.0; + H[i].tdf = CosmicTime[H[i].iscale]; + + if (H[i].type == 2) H[i].mvir = H[H[i].iprog].mvir; + } + + //Reset the arrays for the stellar populations formed at any time in the galaxy and ICM to zero + for (i = 0; i < All.MaxNLeaves * All.NTimesteps * NThread; i++) + { + MassFormed[i] = 0.0; +#ifdef COMPUTE_ICM + ICMFormed[i] = 0.0; +#endif + } + +#if (RANDOM_NUMBER_TABLE == 0) + // start-up seed for gaussian random numbers + gsl_rng_set(rng_gaussian, 140314081082 + (long)(ThisTask % All.NTaskPerUniverse)); +#endif + + //Reset the model statistics to zero + for (i = 0; i < All.NTimesteps * All.Nmstar * NThread; i++) + { + Modelsmf[i] = 0.0; + Modelsmfred[i] = 0.0; + Modelssfr[i] = 0.0; + } + for (i = 0; i < All.NTimesteps * NThread; i++) Modelcsfrd[i] = 0.0; + +} + + +/*! \brief This function creates a galaxy in each dark matter halo + * + * This function goes through all trees/forests and haloes and forms a galaxy. It is a wrapper around + * assemble_tree, which populates haloes in individual trees/forests with galaxies. Depending on whether + * #COMPUTE_ICM is set, the function loops over trees and calls assemble_tree with itrees or loops over + * forests and calls assemble_tree with iforest. + */ +void assemble_galaxies(void) +{ +#ifndef COMPUTE_ICM + int itree; +#else + int iforest; +#endif + int thisthread = 0; + + //If we do not need the ICM we can go tree-by-tree +#ifndef COMPUTE_ICM + //Loop through all trees +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel private(thisthread) + { + thisthread = omp_get_thread_num(); +#pragma omp for schedule(dynamic,100) +#endif + for (itree = 0; itree < Ntrees; itree++) assemble_tree(itree,thisthread); +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +} +#endif + //If we do need the ICM we must go forest-by-forest +#else + //Loop through all forests +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel private(thisthread) + { + thisthread = omp_get_thread_num(); +#pragma omp for schedule(dynamic,100) +#endif + for (iforest = 0; iforest < Nforests; iforest++) assemble_tree(iforest,thisthread); +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +} +#endif +#endif + +} + + +/*! \brief This function populates haloes in a tree or forest with galaxies + * + * This function goes through a specific tree/forest and haloes and forms galaxies. It is first checked if the halo + * is still present or if it merged or got stripped already. Then the dynamical friction time is set or + * possibly reset for orphans. The SFR of each halo is calculated as the product of the efficiency and + * the baryonic accretion rate. If the halo mass has fallen below its peak mass the SFR is kept constant. + * If the quenching time (tau * t_decay) has elapsed the SFR is set to zero. Given the SFR the stellar mass + * that has formed in this timestep in this branch is added to the #MassFormed array at the correct position. + * If a halo becomes a subhalo all its ICM is moved to the host halo. If the virial mass has dropped below + * a fraction of its peak mass (Fstrip * mpeak) the stellar mass is moved to the ICM of the host halo and + * the halo is marked as gone. If the dynamical friction time of an orphan has elapsed we merge it back to + * its main branch and add all stellar mass and ICM to the main galaxy (type 0 or 1 halo). The orphan is + * then marked as gone. Finally the stellar mass and ICM at each timestep is computed from the arrays + * #MassFormed and #ICMFormed given the stellar loss rate that has been stored in a table. At the end of each + * tree/forest the #MassFormed and #ICMFormed arrays are reset to zero. + * + * \param itree Index of the tree or forest that is populated with galaxies + * \param thisthread Number of the OpenMP thread that calculates this tree + */ +void assemble_tree(int itree, int thisthread) +{ + int i, j, ihalo, iprog, imass, itdf, itdfmain, hstart, hend; + float t_now, t_since_peak, t_decay, t_quench, radius, theta, phi, dx, dy, dz; + +#ifndef COMPUTE_ICM + hstart = OffsetHalos[itree]; + hend = OffsetHalos[itree] + NhalosInTree[itree]; +#else + hstart = OffsetHalos[itree]; + hend = OffsetHalos[itree] + NhalosInForest[itree]; +#endif + + //Go through all haloes in this tree/forest + for (ihalo = hstart, imass = 0; ihalo < hend; ihalo++) + { + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Check if galaxy in halo still exists - otherwise skip // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //Check if the halo is still there - otherwise mark its descendant as gone and skip + if (H[ihalo].gone) + { + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].gone = 1; + continue; + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the current cosmic time // +////////////////////////////////////////////////////////////////////////////////////////////////////// + t_now = CosmicTime[H[ihalo].iscale]; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Check if halo is a leaf // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //If this is a leaf increase the leaf index and set iprog to 0 (in case we ever need to call it) + if (H[ihalo].np == 0) + { + imass++; + iprog = 0; + } + //If the halo has at least one progenitor + else + { + //Set its actual iprog index + iprog = H[ihalo].iprog; +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Check if halo is an orphan and possibly reset the dynamical friction time // +////////////////////////////////////////////////////////////////////////////////////////////////////// + if (H[ihalo].type == 2) + { + //If the halo has become an orphan this timestep + //Set its dynamical friction time using iprog as satellite and H[imain].iprog as central + if (H[iprog].type != 2) + { + H[ihalo].tdf = CosmicTime[H[iprog].iscale] + get_tdf(iprog,H[H[ihalo].imain].iprog); + H[ihalo].itdf = iprog; + } + //If the halo is not the first orphan - check if merger clock needs reset + //If the progenitor's main's descendant is an orphan - if so reset with H[iprog].imain as sat and H[imain].iprog as cen + else if (H[H[H[iprog].imain].idesc].type == 2) + { + //H[ihalo].tdf = CosmicTime[H[iprog].iscale] + get_tdf(H[iprog].imain,H[H[ihalo].imain].iprog); + H[ihalo].tdf = CosmicTime[H[iprog].iscale] + get_tdf(iprog,H[H[ihalo].imain].iprog); + H[ihalo].itdf = iprog; + } //End recomputation of tdf + } //End Orphan + } //End leaf/no leaf + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Check if halo is an orphan, compute position and inherit the dynamical friction time // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //If the halo is an orphan set its descandants dynamical friction time + if (H[ihalo].type == 2) + { //Get index of progenitors when tdf was computed + itdf = H[ihalo].itdf; + itdfmain = H[H[H[itdf].idesc].imain].iprog; +#ifdef ORPHAN_MASSLOSS + //Let the halo mass of the orphan decay on a timescale tdf-tpeak + if (H[itdf].iscale > H[H[ihalo].impeak].iscale) + { + H[ihalo].mvir = H[iprog].mvir * pow(H[H[ihalo].impeak].mvir/H[itdf].mvir, -Timestep[H[ihalo].iscale]/(CosmicTime[H[itdf].iscale] - CosmicTime[H[H[ihalo].impeak].iscale])); + } + else H[ihalo].mvir = H[iprog].mvir; +#endif + //Compute the initial radius scaled with respect to the dynamical friction time + dx = NEAREST(H[itdf].pos[0] - H[itdfmain].pos[0]); + dy = NEAREST(H[itdf].pos[1] - H[itdfmain].pos[1]); + dz = NEAREST(H[itdf].pos[2] - H[itdfmain].pos[2]); + radius = sqrt(dx*dx+dy*dy+dz*dz) * sqrt((H[ihalo].tdf-t_now)/(H[ihalo].tdf-CosmicTime[H[itdf].iscale])); +#ifdef ORPHAN_NONRADIAL_INFALL + theta = get_uniform_random_number(ihalo) * M_PI; + phi = get_uniform_random_number(ihalo + RANDOM_NUMBER_TABLE/2) * 2.0 * M_PI; +#else + theta = get_uniform_random_number(itdf) * M_PI; + phi = get_uniform_random_number(itdf + RANDOM_NUMBER_TABLE/2) * 2.0 * M_PI; +#endif + //Position the orphan on a sphere with r=radius around imain + H[ihalo].pos[0] = H[H[ihalo].imain].pos[0] + radius * cos(phi) * sin(theta); + H[ihalo].pos[1] = H[H[ihalo].imain].pos[1] + radius * sin(phi) * sin(theta); + H[ihalo].pos[2] = H[H[ihalo].imain].pos[2] + radius * cos(theta); + if (H[ihalo].pos[0] >= All.Lbox) H[ihalo].pos[0] -= All.Lbox; + if (H[ihalo].pos[1] >= All.Lbox) H[ihalo].pos[1] -= All.Lbox; + if (H[ihalo].pos[2] >= All.Lbox) H[ihalo].pos[2] -= All.Lbox; + if (H[ihalo].pos[0] < 0.0) H[ihalo].pos[0] += All.Lbox; + if (H[ihalo].pos[1] < 0.0) H[ihalo].pos[1] += All.Lbox; + if (H[ihalo].pos[2] < 0.0) H[ihalo].pos[2] += All.Lbox; + + //If there is a descendent let it inherit the dynamical friction time and the index when it was computed + if (H[ihalo].idesc >= 0) + { + H[H[ihalo].idesc].tdf = H[ihalo].tdf; + H[H[ihalo].idesc].itdf = H[ihalo].itdf; + } + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute relevant time scales // +////////////////////////////////////////////////////////////////////////////////////////////////////// + // Compute time that has elapsed since peak mass was reached + t_since_peak = t_now - CosmicTime[H[H[ihalo].impeak].iscale]; + // Compute timescale on which sfr declines, here: dynamical time of the halo when peak mass was reached + t_decay = DynTime[H[H[ihalo].impeak].iscale]; + +#ifdef SAT_QUENCH_MASS_DEPENDENT + // Get the slope for the quenching timescale + if (H[H[ihalo].impeak].mstar > 0) t_quench = t_decay * P.Tau0 * pow(H[H[ihalo].impeak].mstar*All.m_unit/1.0e10,-P.TauS); + else t_quench = 2.0 * t_since_peak; + if (t_quench < t_decay * P.Tau0) t_quench = t_decay * P.Tau0; +#else + t_quench = P.Tau0 * t_decay; +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute star formation rate from efficiency and growth rate // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //Set the SFR equal to the efficiency times the baryonic accretion rate + H[ihalo].sfr = sfe(H[ihalo].mvir,H[ihalo].a) * H[ihalo].mdotbary; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Satellite keeps forming stars at constant rate // +////////////////////////////////////////////////////////////////////////////////////////////////////// + // If Halo does not gain mass (passed peak mass) keep SFR constant + if (H[ihalo].mvir < H[H[ihalo].impeak].mvir) H[ihalo].sfr = H[iprog].sfr; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Satellite keeps forming stars at declining rate // +////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef SAT_SFR_EXP_DECAY + // If there is an exponential decline in the sfr after peak mass reduce current sfr + if (H[ihalo].mvir < H[H[ihalo].impeak].mvir) H[ihalo].sfr = H[H[ihalo].impeak].sfr * exp(-P.TauD * t_since_peak / t_decay); +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Satellite quenching // +////////////////////////////////////////////////////////////////////////////////////////////////////// + // If certain amount of time after tpeak has elapsed, quench galaxy + if (t_since_peak > t_quench) H[ihalo].sfr = 0.0; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Form stellar population for this scale factor and store in MassFormed array // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //Add the mass that has formed this timestep (sfr*dt) to the mass array + MassFormed[H[ihalo].imass*All.NTimesteps+H[ihalo].iscale+thisthread*All.NMassFormed] + += H[ihalo].sfr * Timestep[H[ihalo].iscale]; + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Add ICM of halo that becomes a subhalo to the main halo // +////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef COMPUTE_ICM + // Add ICM from satellites in a host halo to the host's ICM if a galaxy becomes a satellite + if (H[ihalo].type != 0 && H[iprog].type == 0 && H[ihalo].np > 0) + { + for (j = 0; j <= H[ihalo].iscale; j++) + { + //Add the ICM of the satellite to the main halo + ICMFormed[H[H[ihalo].ihost].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + += ICMFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed]; + //Set the ICM array to zero so there is no ICM left in descendant subhaloes + ICMFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed] = 0.0; + } + } +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Satellite stripping // +////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef SAT_STRIP_USE_MSTAR + // STRIPPING: If halo has lost mass down to some fraction fstrip * mpeak assume that the stellar mass is stripped and goes to the hosts icm + if (H[ihalo].mvir < P.Fstrip * H[H[ihalo].impeak].mvir) +#else + //Alternative: If halo has lost mass down to some fraction fstrip * mstar assume that the stellar mass is stripped and goes to the hosts icm + if (H[ihalo].mvir < P.Fstrip * H[iprog].mstar && H[ihalo].np > 0) +#endif + { +#ifdef COMPUTE_ICM + //If the host is on this task + for (j = 0; j <= H[ihalo].iscale; j++) + { + //Add the stellar mass of the satellite to the main halo's ICM + ICMFormed[H[H[ihalo].ihost].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + += MassFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + + ICMFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed]; + } + // descid points to where icm is distributed + H[iprog].descid = H[H[ihalo].ihost].haloid; +#endif + //If stripped remove galaxy from run + H[ihalo].gone = 1; + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].gone = 1; + H[iprog].idescgal = -1; + continue; + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Satellite merging // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //If the dynamical friction time has run out merge the orphan + if (H[ihalo].tdf < t_now && H[ihalo].type == 2) + { + //Check if the main galaxy is gone + if (H[H[ihalo].imain].gone == 1) + { +#ifdef COMPUTE_ICM + //Go through all formed populations up to now + for (j = 0; j <= H[ihalo].iscale; j++) + { + //Add the stellar mass of the satellite to the main halo's ICM + ICMFormed[H[H[ihalo].ihost].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + += MassFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + + ICMFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed]; + } +#endif + //Remove satellite galaxy from run + H[ihalo].gone = 1; + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].gone = 1; + H[iprog].idescgal = -1; + continue; + } + //Otherwise add all formed populations up to now to main galaxy and its ICM + for (j = 0; j <= H[ihalo].iscale; j++) + { + //Add mass to stellar mass of main descendant + MassFormed[H[H[ihalo].imain].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + += (1.0 - P.Fesc) * MassFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed]; +#ifdef COMPUTE_ICM + //Add mass to ICM of main descendant + ICMFormed[H[H[H[ihalo].imain].ihost].imass*All.NTimesteps+j+thisthread*All.NMassFormed] + += P.Fesc * MassFormed[H[ihalo].imass*All.NTimesteps+j+thisthread*All.NMassFormed]; +#endif + } + //Set the galaxy descandant ID of the progenitor to the first galaxy's index (orphans always have a progenitor) + iprog = H[ihalo].iprog; + H[iprog].idescgal = H[ihalo].imain; + //After merging set the descendant ID to that of main halo(galaxy) + H[iprog].descid = H[H[ihalo].imain].haloid; + //Increase the number of progenitors for the galaxy it merges with + H[H[ihalo].imain].np++; + //Set the progenitor's mmp to zero + H[iprog].mmp = 0; + //Go to the first progenitor of the halo's descendant + iprog = H[H[ihalo].imain].iprog; + //While the icoprog is already used set the iprog to the next co-progenitor + while (H[iprog].icoprog >= 0) iprog = H[iprog].icoprog; + //As soon as icoprog is free write the halo index to the icoprog + H[iprog].icoprog = H[ihalo].iprog; + //Mark the galaxy as gone and if has a descendant mark it as gone as well + H[ihalo].gone = 1; + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].gone = 1; + continue; + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute total stellar mass and ICM from populations // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //Compute the stellar mass from the formed stellar populations + H[ihalo].mstar = stellar_mass(H[ihalo].imass,H[ihalo].iscale,thisthread); +#ifdef COMPUTE_ICM + //Compute the intra cluster mass from the formed stellar populations (only for main haloes) + if (H[ihalo].type == 0) H[ihalo].icm = intra_cluster_mass(H[ihalo].imass,H[ihalo].iscale,thisthread); + else H[ihalo].icm = 0.0; +#endif + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Add this galaxy to the global statistics // +////////////////////////////////////////////////////////////////////////////////////////////////////// + add_galaxy_to_statistics(ihalo,thisthread); + + }//End loop through all haloes - increment ihalo + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// Reset MassFormed and ICMFormed to zero // +////////////////////////////////////////////////////////////////////////////////////////////////////// + //After the tree/forest is done reset the MassFormed array to 0 + for (i = 0; i < imass * All.NTimesteps; i++) + { + MassFormed[i+thisthread*All.NMassFormed] = 0.0; +#ifdef COMPUTE_ICM + ICMFormed[i+thisthread*All.NMassFormed] = 0.0; +#endif + } + +} + + +/*! \brief This function is the heart of the model + * + * This function returns the instantaneous baryon conversion efficiency for a given halo mass and scale + * factor. The parameters are those stored on each task so it should be made sure that in a given + * universe all tasks have the same values for the parameters. + * The instantaneous baryon conversion efficiency is defined as: + * \f[ \epsilon(M,z) = 2 \;\epsilon_\mathrm{N} \left[ \left(\frac{M}{M_1}\right)^{-\beta} + + * \left(\frac{M}{M_1}\right)^{\gamma}\right]^{-1} \; ,\f] + * where \f$M_1\f$,\f$\epsilon_\mathrm{N}\f$, \f$\beta\f$, and \f$\gamma\f$ depend on redshift and are + * given by: + * \f{eqnarray*}{ + * \log_{10} M_1(z) &=& M_0 + M_\mathrm{z}\frac{z}{z+1}\\ + * \epsilon_\mathrm{N}(z) &=& \epsilon_0 + \epsilon_\mathrm{z}\frac{z}{z+1}\\ + * \beta(z) &=& \beta_0 + \beta_\mathrm{z}\frac{z}{z+1}\\ + * \gamma(z) &=& \gamma_0 + \gamma_\mathrm{z}\frac{z}{z+1} \; . + * \f} + * + * \param hmass Virial mass of the dark matter halo in code units + * \param a Scale factor at which the dark matter halo is located + */ +float sfe(float hmass, float a) +{ + //Possibly make static inline? + double sfe_e,sfe_m,sfe_b,sfe_g; + sfe_e = P.Epsilon0 + P.EpsilonZ * (1.-a); + sfe_m = pow(10., P.M0 + P.MZ * (1.-a))/All.m_unit; + sfe_b = P.Beta0 + P.BetaZ * (1.-a); + sfe_g = P.Gamma0 + P.GammaZ * (1.-a); + return (float)(2.0*sfe_e/(pow(hmass/sfe_m,-sfe_b)+pow(hmass/sfe_m,sfe_g))); +} + + +/*! \brief This function calculates the stellar mass from the #MassFormed array + * + * This function returns the stellar mass in a given branch imass. The elements in the #MassFormed array + * are summed up from 0 to the current time specified by the index of the scale factor array scalemax. + * Each element is multiplied by the fraction of stars still left at this time which is stored in #MassLeft. + * + * \param imass The branch of the tree for which the stellar mass is calculated + * \param scalemax The index of the scale factor array up to which the stellar mass is calculated. + * \param thisthread Number of the OpenMP thread that is responsible + */ +float stellar_mass(int imass, int scalemax, int thisthread) +{ + int i; + float mass = 0.0; + //Set the thread number + for (i = 0; i <= scalemax; i++) + mass += MassFormed[imass*All.NTimesteps+i+thisthread*All.NMassFormed] * MassLeft[scalemax*All.NTimesteps+i]; + return mass; +} + + +/*! \brief This function calculates the ICM from the #ICMFormed array + * + * This function returns the ICM in a given branch imass. The elements in the #ICMFormed array + * are summed up from 0 to the current time specified by the index of the scale factor array scalemax. + * Each element is multiplied by the fraction of stars still left at this time which is stored in #MassLeft. + * + * \param imass The branch of the tree for which the ICM is calculated + * \param scalemax The index of the scale factor array up to which the ICM is calculated. + * \param thisthread Number of the OpenMP thread that is responsible + */ +float intra_cluster_mass(int imass, int scalemax, int thisthread) +{ + int i; + float mass = 0.0; + for (i = 0; i <= scalemax; i++) + mass += ICMFormed[imass*All.NTimesteps+i+thisthread*All.NMassFormed] * MassLeft[scalemax*All.NTimesteps+i]; + return mass; +} + + +/*! \brief This function calculates the dynamical friction time of an orphan + * + * This function returns the dynamical friction time of an orphan ihalo merging with a main galaxy imain. + * It either uses the default dynamical friction formula by + * Binney & Tremain (1987) or one based on + * B Boylan-Kolchin et al. (2008), which can be enabled by the + * option #DF_USE_BK08. For BT97 a factor of 2 is used to account for the longer merging times + * found by others. For BK08 the power d of the satellite radius is set to 2 since local satellite mass is used + * (not the one at infall). For both, the stellar mass of both galaxies is taken into account as well when + * calculating the dynamical frition time. + + * \param ihalo The index of the orphan halo that is merging + * \param imain The index of the main (non-orphan) halo the orphan is merging with + */ +float get_tdf(int ihalo, int imain) +{ +#define G 4.30071e-9 ///< Gravitational constant in (km/s)^2 * Mpc / Msol +#define MPC_IN_KM 3.08568e19 ///< Megaparsecs in kilometers +#define YR_IN_SEC 31556926.0 ///< Year in seconds + + double vvir, rsat, mu, eta, tau, clog, dx, dy, dz; + + //Compute virial velocity, current radius, satellite mass and Coulomb logarithm + vvir = sqrt(G*H[imain].mvir*All.m_unit/H[imain].rvir/All.x_unit); + dx = NEAREST(H[ihalo].pos[0]-H[imain].pos[0]); + dy = NEAREST(H[ihalo].pos[1]-H[imain].pos[1]); + dz = NEAREST(H[ihalo].pos[2]-H[imain].pos[2]); + rsat = sqrt(dx*dx+dy*dy+dz*dz); + clog = log(1.0+(H[imain].mvir+H[imain].mstar)/(H[ihalo].mvir+H[ihalo].mstar)); + mu = (H[ihalo].mvir+H[ihalo].mstar)/(H[imain].mvir+H[imain].mstar); + tau = (H[imain].rvir*All.x_unit/vvir) * MPC_IN_KM / YR_IN_SEC / All.t_unit; + +#ifdef DF_USE_BK08 + eta = 0.5 + 0.214 * get_gaussian_random_number(ihalo+imain); //Zentner et al. (2005) + if (eta < 0.0) eta = 0.0; + if (eta > 1.0) eta = 1.0; + //Use a modified version of the Boylan-Kolchin formula (see section 3.3.2). + //d=2 is used because we do not take the satellite mass when it was at the virial radius but the current one + const float a = 0.216; + const float b = 1.3; + const float c = 1.9; + const float d = 2.0; +#else + eta = 1.0; + //Use formulat from Binney & Tremain with a factor of 2 + const float a = 2.0 * 1.17; + const float b = 1.0; + const float c = 0.0; + const float d = 2.0; +#endif + + return a / clog * pow(mu,-b) * exp(c*eta) * pow(rsat/H[imain].rvir,d) * tau; + +#undef YR_IN_SEC +#undef MPC_IN_KM +#undef G +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..9ee9bb9 --- /dev/null +++ b/src/main.c @@ -0,0 +1,1558 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////// EMERGE main program /////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2013-2017 Benjamin P. Moster // +/////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////// Version 1.0.0 ////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////// September 1st 2019 //////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// Empirical ModEl for the foRmation of GalaxiEs // +// // +/////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// This program is free software: you can redistribute it and/or modify it under the terms of // +// the GNU General Public License as published by the Free Software Foundation, either // +// version 3 of the License, or (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // +// See the GNU General Public License for more details. // +// // +// You should have received a copy of the GNU General Public License along with this program // +// in a file called "LICENSE.txt". If not, see . // +// // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file main.c +/// \brief This file contains the function #main() that starts the program +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + +/*! \brief The entry point of the program. + * + * This function initializes the MPI communication packages, and the OpenMP threads. + * Then #read_parameterfile() is called and the parameter file is read. Then the run + * is set up by calling #setup(), #read_data(), #read_trees(), and #init_galaxies(). + * Then the main program is started depending on the selected mode. After that the + * program is finalised by calling #finish_galaxies(), finalize(), and MPI_Finalize(). + */ +int main(int argc, char* argv[]) +{ + + char parameterfile[NSTRING]; + int i; + + //Initialise MPI + MPI_Init(&argc, &argv); + MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask); + MPI_Comm_size(MPI_COMM_WORLD, &NTask); + MPI_Barrier(MPI_COMM_WORLD); + +//If OpenMP is set in Makefile and OPENMPTHREADS is set in Config.sh set the number of threads + NThread = 1; +#if defined(_OPENMP) && (OPENMPTHREADS > 1) + NThread = OPENMPTHREADS; + omp_set_num_threads(OPENMPTHREADS); +#endif + + //Define All.fullline and All.startline + for (i=0; i 1) + printf("%s Using %d OpenMP threads per task\n",All.startline,NThread); +#endif + } + + //Print compile time options that have been used + if(ThisTask == 0) { + printf("%s\n",All.fullline); + printf("%s %s has been compiled with these settings:\n",All.startline,CODE); + output_code_options(); + printf("%s Size of halo structure %d [bytes]\n",All.startline, (int) sizeof(struct halo)); + printf("%s\n",All.fullline); + } + + //If less than 1 argument is used remind that a parameter file needs to be passed + if(argc < 2) { + if(ThisTask == 0) { + printf("%s Parameters are missing.\n",All.startline); + printf("%s Call with []\n",All.startline); + printf("%s\n",All.startline); + printf("%s RestartFlag Action\n",All.startline); + printf("%s 0 Read merger trees and run model once\n",All.startline); + printf("%s 1 Start affine invariant ensemble sampler MCMC\n",All.startline); + printf("%s 11 Restart affine invariant ensemble sampler MCMC\n",All.startline); + printf("%s 12 Compute statistics for each MCMC walker\n",All.startline); + printf("%s 2 Start HYBRID optimization method\n",All.startline); + printf("%s 21 Restart HYBRID optimization method\n",All.startline); + printf("%s 22 Compute statistics for each HYBRID walker\n",All.startline); + printf("%s 3 Start parallel tempering\n",All.startline); + printf("%s 31 Restart parallel tempering\n",All.startline); + printf("%s 32 Compute statistics for each parallel tempering walker\n",All.startline); + printf("%s\n",All.startline); + } + endrun("Restart code with parameter file."); + } + + All.Mode = 0; + if(argc > 2) All.Mode = atoi(argv[2]); + + //First read the parameter file + sprintf(parameterfile,"%s",argv[1]); + if (ThisTask == 0) printf("%s Reading parameters from %s...\n",All.startline,parameterfile); + read_parameterfile(parameterfile); + + //Setup all arrays and variables + setup(); + + //Read the observed data + read_data(); + + //Load all trees to memory and derive a few properties + read_trees(All.treefile_name); + + //Initialise all galaxy related variables + init_galaxies(); + + //Now check what shall be done + if (All.Mode == 0) + //For mode 0 we assemble the galaxies once and print their statistics and the catalogue + { + //Populate haloes with galaxies + make_galaxies(); + + //Get statistics + get_statistics(); + + //Write statistics + write_statistics(); + + //Write out chi^2 of 1st Universe + print_chi2(); + +#ifdef WRITE_GALAXY_CATALOG + //Output Galaxies + output_galaxies(); +#endif + +#ifdef WRITE_HALO_CATALOG + //Output Haloes + output_halos(); +#endif + +#ifdef WRITE_MAINBRANCH + //Output Haloes + output_mainbranch(); +#endif + + } + + //For mode 1 we start the affine invariant ensemble sampler MCMC + if (All.Mode == 1) aies_mcmc(0); + //For mode 11 we restart the affine invariant ensemble sampler MCMC + if (All.Mode == 11) aies_mcmc(1); + //For mode 2 we start the HYBRID optimization method + if (All.Mode == 2) optimize_hybrid(0); + //For mode 21 we restart the HYBRID optimization method + if (All.Mode == 21) optimize_hybrid(1); + //For mode 3 we start the parallel tempering method + if (All.Mode == 3) parallel_tempering(0); + //For mode 31 we restart the parallel tempering method + if (All.Mode == 31) parallel_tempering(1); + //For mode 12, 22, and 32 we compute statistics for each walker + if (All.Mode == 12 || All.Mode == 22 || All.Mode == 32) write_walker_statistics(); + + //Free all galaxy related arrays + finish_galaxies(); + + //Goodbye + if(ThisTask == 0) printf("%s\n%s Thank you for selecting EMERGE to create your galaxies! So long...\n%s\n",All.fullline,All.startline,All.fullline); + + finalize(); + + MPI_Finalize(); // clean up & finalize MPI + + return 0; + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// The rest of this file contains documentation for compiling and running the code, // +// in a format appropriate for 'doxygen'. // +// // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// Documentation +/*! \mainpage Introduction + + Emerge, an Empirical ModEl for the foRmation of GalaxiEs, populates dark matter halo merger trees + with galaxies using simple empirical relations between galaxy and halo properties. For each model + represented by a set of parameters, it computes a mock universe, which it then compares to + observed statistical data to obtain a likelihood. Parameter space can be explored with several + advanced stochastic algorithms such as MCMC to find the models that are in agreement with the + observations. + + The code is written in ANSI C and supports massively parallel computers with distributed memory. + Emerge uses an explicit communication model that is implemented with the standardized message + passing interface (MPI) and suports hyperthreading via the OpenMP interface. It can be run on + essentially all supercomputer systems, including clusters of workstations or individual personal + computers. + + The main reference for the scientific methods that are used in this code is the paper + `Emerge - An empirical model for the formation of galaxies since z ~ 10' (Moster, Naab & + White 2018), and references therein. This document provides additional technical information + about the code to allow anyone interested an independent use of the code on parallel machines, + and to create mock galaxy catalogues for any dark matter simulation. For further help, visit + the code website at [usm.lmu.de/emerge](http://www.usm.lmu.de/emerge) or join the Gitter + community at [gitter.im/emerge-code](https://gitter.im/emerge-code). + + Emerge was written by Benjamin Moster, and is made publicly available under the GNU general + public license version 3. This implies that you may freely copy, distribute, or modify the sources, + but the copyright for the original code remains with the author. If you find the code useful for + your scientific work, we kindly ask you to include a reference to the Emerge paper in all studies + that use data obtained with Emerge. + + + \page usage Basic Usage + + \section download Obtaining the code + + Emerge is hosted on [GitHub](https://github.com/bmoster/emerge) and can be obtained either + by cloning the git repository + + git clone https://github.com/bmoster/emerge.git + + or by downloading a compressed tarball of the latest version: + + https://github.com/bmoster/emerge/archive/master.zip (~ __700kB__). + + + \section compile Compiling + + Emerge needs the following non-standard libraries for compilation: + - __MPI__ - the Message Passing Interface. There are numerous commercial versions, in addition to + excellent open source implementations, e.g.\n MPICH (http://www.mpich.org), or\n Open MPI (http://www.open-mpi.org). + - __GSL__ - the GNU scientific library. This open-source package can be obtained at\n http://www.gnu.org/software/gsl, + for example. Emerge needs this library for spline interpolations, and for random number generation. + - __HDF5__ - the Hierarchical Data Format (version 5.0 or higher, available at\n https://www.hdfgroup.org). + This optional library is only needed when one wants to write snapshot files in HDF5 format. + It is possible to compile and use the code without this library. + + On most UNIX systems installing these libraries should only take a few steps. Untar the downloaded tarball + and change to the newly created directory. Run the configuration script by typing `./configure`, compile the code + by typing `make all`, and install the libraries by tyiping `sudo make install`. + + After downloading the Emerge code, you will find several files and folders: + The `build` folder is initially empty, but will host the object files after compilation. + The `data` folder contains all files that store observed data (see \ref datafiles for more details). + The `output` folder is initially empty, but all output data will be written to this folder, see \ref outputfiles for more details. + The `parameterfiles` folder contains an example parameter file (see \ref parameterfile for more details). + The `src` folder contains the source code including all c-files and h-files. + The `tools` folder contains files that convert Rockstar halo merger trees to the Emerge binary input format, and a sample + script file to submit to a computing queue. + The `trees` folder is initially empty and can be used to store the input halo merger trees. + The files `compile-config.perl` and `config-makefile` are needed for compiation, but do not need to be edited. + The file `Template-Config.sh` has to be copied to `Config.sh` and contains all compile-time options. Edit `Config.sh` + as needed (see \ref configfile for more details). + The file `Template-TypeOfSystem.sh` has to be copied to `TypeOfSystem.sh` and contains a number of predefined systems. + The appropriate system should be uncommented in `TypeOfSystem.sh`. By following these example systems, further customised + system-types can be easily added to the `Makefile`. Slight adjustments will be needed if any of the above libraries is not + installed in a standard location on the system. Also, compiler optimisation options may need adjustment, depending on the + C-compiler that is used. The executable `emerge` can then be built by typing `make`. + + + \section run Running the code + + To run Emerge, invoke the executable with a command like + + mpiexec -np 16 ./emerge emerge.param + + This will start emerge using 16 computing cores and with parameters as specified in the parameter file `emerge.param` + (see \ref parameterfile for more details). The code does not need to be recompiled for a different number + of processors, for different parameters, or for different halo marger trees. Emerge can also be run with a single computing + core given enough memory for the problem size. To this end, the leading `mpiexec -np 1` can be omitted such that Emerge + behaves like a serial code. The MPI library must be installed in any case as it is frequently referenced in the source code. + The number of processors can be chosen freely, however it must be a multiple of the number of universes that are computed at + the same time. This means that if for example 20 universes are computed in the same step (e.g. for the parallelised MCMC), + at least 20 cores are needed, or a multiple thereof. + + While Emerge is running, it prints out several log messages that inform about the present steps taken by the code. + If Emerge is used interactively in a terminal these messages appear on the screen. They can be redirected to a file by adding + `> log.out` at the end of the execution command. If Emerge is used to explore parameter space, or if the problem size is too + large to fit on a single computing node, it will typically need to be run on a supercomputer. In this case, the above command + has to be put into a script file that can be submitted to the computing queue, and the output should be diverted to a file. + The sample script file `submit` in the `tools` folder shows how to do this for the SuperMuc computer at the LRZ, but will need + to be adapted for a different supercomputer. + + + \section mode Different code modes + + Invoking the execution command above will run Emerge for the halo merger trees and parameters specified in the parameter file + once, and write the resulting galaxy properties into files in the `output` directory. However, Emerge can also be used to explore + parameter space with different algorithms. To this end, an optional flag can be added to the end of the execution command. The + three currently implemented parameter space exploration methods are: + + Flag | Mode + :--: | ---- + 1 | Affine-invariant MCMC ensemble sampler by Goodman & Weare (2010) + 2 | HYBRID optimisation algorithm by Elson et al. (2007) + 3 | Parallel-tempering as laid out by Sambridge (2014) + + The HYBRID optimisation algorithm for example can thus be started by invoking + + mpiexec -np 16 ./emerge emerge.param 2 + + which will first initialise a number of walkers in a parameter range as specified in the parameter file, and then evolve the walker + positions according to the algorithm and several hyperparameters. The number of walkers, i.e. universes, that are computed during + one chain step and the time limit of the run can also be specified in the parameter file. Each set of chains requires a seed number + to be specified in the parameter file. This number will be included in the name of the output files, such that they can be distinguished, + e.g. for the seed number `12345` the MCMC output file name will be `mcmc12345.000.out`, the HYBRID output file name will be + `hybrid12345.000.out`, and the parallel-tempering output file name will be `pt12345.000.out`. + + Often a single submission to a queueing system will not provide enough time to explore parameter space as thoroughly as needed. + However, all algorithms store the current walker positions in dedicated restart files in the `output` folder, e.g. `walkers.mcmc.12345.out`. + Emerge can then be restarted from these walker positions and the chains will then be continued. To resume parameter space exploration + with a specific algorithm a `1` has to be added to the flag above, e.g. the parallel-tempering algorithm can be restarted from the + latest saved walker positions by invoking + + mpiexec -np 16 ./emerge emerge.param 31 + + The appropriate restart file `walkers.pt.12345.out` has to exist and have the required number of walkers. Once the code restarts, it + will write the walker postions to a new output file, e.g. `pt12345.001.out`. Restarting Emerge in this way is transparent to the code, + i.e. after the restart Emerge behaves exactly as if had not been interrupted in the first place. When the code is started with a restart + flag, the parameter file is parsed, but only some of the parameters are allowed to be changed, while any changes in the others will be + ignored. Which parameters these are is explained in the section \ref parameterfile. + + + \page configfile Configuration file + + Many aspects of the Emerge code are controlled with compile-time options in the Config.sh file rather than run-time options in the + parameter file. In this way, highly optimised binaries for a specific model can be generated by the compiler. However, the disadvantage + of this approach is that different setups may require different binary executables. If several models are run concurrently, there is + hence the danger that Emerge is started or resumed with the `wrong' binary. While Emerge checks the plausibility of some of the most + important code options, this is not done for all of them. To minimise the risk of using the wrong code, it is therefore recommended + to produce a separate executable for each simulation that is run. This can be easily achieved by creating an alternative Config.sh + file for each setup, and then compiling the source code invoking + + make CONFIG=NewConfig.sh EXEC=emerge_new + + The Config.sh file contains a list of all available compile-time code options, with most of them commented out by default. To activate + a certain option, it should be commented in, and given the desired value, where appropriate. Whenever one of the Config.sh options + described below is changed, a full recompilation of the code is necessary, which can be achieved with a simple `make` command. + A manual recompilation of the whole code can be enforced with the command `make clean`, which will erase all object files, followed by `make`. + + + \section config_code Code options + + `OPENMPTHREADS=2` + > Enables OpenMP support and sets the number of threads per task. If the computing system supports hyperthreading the default + > value of 2 is recommended. + + `RANDOM_NUMBER_TABLE=1000000` + > If this option is set, random number tables will be created at the start of the run, for a uniform distribution and a gaussian + > distribution, respectively. The parameter has to be set to the number of random numbers each of these tables will contain. The + > random numbers created in this way will be only used for the scatter between intrinsic and observed galaxy properties and for + > the positions of orphan galaxies within their host haloes, not for the parameter space exploration algorithms. The + > advantage of this option is the increased computation speed, as random numbers do not have to be recomputed for each galaxy in + > each chain step, and the consistent scatter between steps. + + `LONGIDS` + > If this option is set, the code assumes that IDs for haloes, descendants, hosts, and forests, are stored as 64-bit long integers. + > This is only really needed if there are more than ~ 2 billion haloes in the merger trees. + + `DISABLE_MEMORY_MANAGER` + > By default, Emerge is using a memory manager that handles the dynamic memory allocation. The overhead for the allocation and + > dellocation of memory during the run is avoided by allocating a large part of the total available memory during initialisation. + > This section is then filled dynamically by individual memory blocks using a stack structure and a 64 bit boundary. + > If this option is set, the memory manager is disabled, and all memory blocks are allocated on the fly, which may reduce + > the performance of the code. + + + \section config_model Model options + + `SFE_MPEAK_ZEVOLV` + > By default, the instantaneous conversion efficiency \f$ \epsilon(M) = 2 \epsilon_\mathrm{N} [ (M/M_1)^{-\beta} + (M/M_1)^{\gamma}]^{-1} \f$ + > does not depend on redshift. Setting this option makes the characteristic halo mass \f$ M_1 \f$ depend linearly on the scale factor \f$ a \f$: + > \f[M_1(z) = M_0 + M_\mathrm{z}(1-a) = M_0 + M_\mathrm{z} \cdot z/(z+1) \;.\f] + + `SFE_NORM_ZEVOLV` + > Setting this option makes the normalisation of the instantaneous conversion efficiency \f$ \epsilon_\mathrm{N} \f$ depend linearly on the scale + > factor \f$ a \f$: + > \f[\epsilon_\mathrm{N}(z) = \epsilon_0 + \epsilon_\mathrm{z}(1-a) = \epsilon_0 + \epsilon_\mathrm{z} \cdot z/(z+1) \;.\f] + + `SFE_BETA_ZEVOLV` + > Setting this option makes the low-mass slope of the instantaneous conversion efficiency \f$ \beta \f$ depend linearly on the scale + > factor \f$ a \f$: + > \f[\beta(z) = \beta_0 + \beta_\mathrm{z}(1-a) = \beta_0 + \beta_\mathrm{z} \cdot z/(z+1) \;.\f] + + `SFE_GAMMA_ZEVOLV` + > Setting this option makes the high-mass slope of the instantaneous conversion efficiency \f$ \gamma \f$ depend linearly on the scale + > factor \f$ a \f$: + > \f[\gamma(z) = \gamma_0 + \gamma_\mathrm{z}(1-a) = \gamma_0 + \gamma_\mathrm{z} \cdot z/(z+1) \;.\f] + + `SFE_SAME_SLOPE` + > If this option is set, the low and high-mass slopes of the instantaneous conversion efficiency are required to have the same absolute + > value at any redshift, i.e. \f$ {\beta(z)=\gamma(z)} \f$. + + `SAT_QUENCH_MASS_DEPENDENT` + > By default, satellite galaxies are quenched after a fixed time \f$ \tau \f$ once their halo stops growing, given by + > \f$ {\tau = \tau_0 \cdot t_\mathrm{dyn}} \f$, where \f$ t_\mathrm{dyn}=R_\mathrm{vir}/V_\mathrm{vir} \f$ is the dynamical + > time of the halo. If this option is enabled, the quenching time \f$ \tau \f$ depends on the stellar mass of the galaxy, with + > low-mass galaxies having a larger quenching time, given by + > \f[ \tau = \tau_0 \cdot t_\mathrm{dyn} \cdot \mathrm{max} [(m_* / 10^{10}\mathrm{M}_\odot)^{-\tau_\mathrm{s}},1] \;,\f] + > where the quenching slope \f$ \tau_\mathrm{s} \f$ is a free parameter. + + `SAT_STRIP_USE_MSTAR` + > By default, a satellite galaxy gets stripped and its stellar mass added to the ICM of the main halo, if the mass of its subhalo + > drops below a fraction of the peak mass during its history: \f$ {M < f_\mathrm{s} \cdot M_\mathrm{peak}} \f$. If this option is set, + > satellite galaxies are instead stripped if the mass of their subhalo drops below a fraction of their own stellar mass: + > \f$ {M < f_\mathrm{s} \cdot m_*} \f$. + + `SAT_SFR_EXP_DECAY` + > If this option is selected, the star formation rate of a galaxy declines exponentially on a timescale \f$ \tau_\mathrm{d} \f$ after + > its halo has reached its peak mass: + > \f[ {\dot m_* = \dot m_{*,\mathrm{peak}} \cdot \exp [-\tau_\mathrm{d}\,(t-t_\mathrm{peak})\,/ \, t_\mathrm{dyn}]} \; .\f] + + `COMPUTE_ICM` + > This option enables the computation of the ICM, and requires the halo merger trees to be organised in forests. A forest contains all + > merger trees that are in contact through their history, i.e. if a halo is located within the virial radius of another halo at any point + > in time, their merger trees are part of the same forest. To be able to handle forests, Emerge requires a forest file with the same base + > file name as the merger tree files and a `.forests` file extension. This file has to store the tree ID for each merger tree (first column), + > and the corresponding forest ID (second column). Emerge then distributes the merger trees to computing cores such that each forest is + > loaded on a single core. These additional sorting processes require somewhat more time at the start of Emerge. + + `DF_USE_BK08` + > If this option is set, Emerge uses the dynamical friction formula by Boylan-Kolchin et al. (2008). Otherwise, the standard formula + > by Binney & Tremaine (1987) is used. + + `ORPHAN_MASSLOSS` + > This option only affects orphan galaxies, i.e. galaxies for which the corresponding subhalo has dropped below the resolution limit of + > the dark matter simulation. If enabled, the halo associated with an orphan galaxy loses mass at the same rate as it did between + > reaching its peak mass and dropping below the resolution limit. Otherwise, the halo mass of an orphan galaxy stays constant at the + > mass when the halo got lost. + + `ORPHAN_NONRADIAL_INFALL` + > By default, the position of orphan galaxies is chosen to be on fixed angular coordinates with respect to the central galaxy, while + > the radius decreases with the dynamical friction time. If this option is set, the angular coordinates are no longer fixed, but chosen + > randomly on the unit sphere at each time step. + + + \section config_data Data options + + `READ_SMF` + > If this option is set, the stellar mass functions are read from the corresponding file in the `data` folder, and included in the + > computation of the model likelihood. + + `READ_FQ` + > If this option is set, the fractions of quenched galaxies as a function of stellar mass are read from the corresponding file in + > the `data` folder, and included in the computation of the model likelihood. + + `READ_CSFRD` + > If this option is set, the cosmic star formation rate densities are read from the corresponding file in the `data` folder, and + > included in the computation of the model likelihood. + + `READ_SSFR` + > If this option is set, the specific star formation rates as a function of stellar mass are read from the corresponding file in + > the `data` folder, and included in the computation of the model likelihood. + + `READ_WP` + > If this option is set, the projected galaxy auto-correlation functions are read from the corresponding file in the `data` folder, + > and included in the computation of the model likelihood. + + `GLOBAL_SIGMA_SMF` + > If this option is set, a global error between different measurements of the stellar mass function can be specified in the parameter + > file. This value will then be added in quadrature to the uncertainty of each individual data point. + + `GLOBAL_SIGMA_FQ` + > If this option is set, a global error between different measurements of the quenched fraction can be specified in the parameter + > file. This value will then be added in quadrature to the uncertainty of each individual data point. + + `GLOBAL_SIGMA_CSFRD` + > If this option is set, a global error between different measurements of the cosmic star formation rate density can be specified in + > the parameter file. This value will then be added in quadrature to the uncertainty of each individual data point. + + `GLOBAL_SIGMA_SSFR` + > If this option is set, a global error between different measurements of the specific star formation rate can be specified in the parameter + > file. This value will then be added in quadrature to the uncertainty of each individual data point. + + `GLOBAL_SIGMA_WP` + > If this option is set, a global error between different measurements of the projected galaxy auto-correlation function can be + > specified in the parameter file. This value will then be added in quadrature to the uncertainty of each individual data point. + + + \section config_model Correlation function options + + `WP_RBINS=20` + > To compute the projected galaxy auto-correlation function, first the 3D auto-correlation function is computed. This option can be + > used to override the default number of 20 radial bins. + + `WP_RBINS_INT=1000` + > The projected auto-correlation function is integrated from the 3D function using interpolation. This option can be used to override + > the default number of 1000 interpolation points. + + `WP_RMAX=0.1` + > The correlation function is computed up to a maximum distance of `WP_RMAX` times the box side length. This option can be used to override + > the default value of 0.1. + + `WP_NLEAF_MIN=4` + > The pair counting needed to compute the correlation function uses a kd-tree. While building this tree, a node is split if it contains + > more than 4 objects. This option can be used to override the default value. + + `WP_NODE_WIDTH_MIN=0.01` + > The splitting of nodes while building the kd-tree needed for the correlation function is also stopped, if the widest dimension is + > smaller than 0.01 times the box side length. This option can be used to override the default value. + + + \section config_fit Fitting options + + The HYBRID optimisation algorithm uses a combination of particle swarm optimisation and simulated annealing, and the step size depends + on the likelihood of the walker with respect to the other walkers and the starting likelihood. At each MCMC step \f$ i \f$, + the mean \f$ \chi^2 \f$ value is computed and compared to the mean starting \f$ \chi^2 \f$ value: + \f$ q_i=\langle\chi_i^2\rangle/\langle\chi_1^2\rangle \f$. Moreover, the \f$ \chi^2 \f$ value of each individual walker \f$ j \f$ is compared + to the mean \f$ \chi^2 \f$ value: \f$ p_j=\langle\chi_j^2\rangle-\chi_j^2 \f$. The step size of each walker is then chosen as + \f$ \sigma = \sigma_0(T) \cdot g(q) \cdot f(p) \f$, where \f$ \sigma(T) \f$ is a normalisation that can depend on the chain temperature, + and \f$ g \f$ and \f$ f \f$ are monotonic functions. The dependence on \f$ q \f$ is implemented as \f$ g(q)=q^\alpha \f$, while the dependence + on \f$ p \f$ is implemented as \f$ f(p)=1-\beta p \f$ for \f$ p<0 \f$ and \f$ f(p)=(p+1)^{-\gamma} \f$ for \f$ p\ge 0 \f$.\n\n + + `HYBRID_ALPHA=0.4` + > This option can be set to override the default value of 0.4 for the slope \f$ \alpha \f$ of the function \f$ g(q) \f$. For a larger value, + > the step size will depend more strongly on the ratio between current and initial mean likelihood. + + `HYBRID_BETA=1.0` + > This option can be set to override the default value of 1.0 for the slope \f$ \beta \f$ of the function \f$ f(p) \f$ for \f$ p<0 \f$. For a + > larger value the step size will depend more strongly on the difference between the likelihood of the walker and the current mean likelihood. + + `HYBRID_GAMMA=1.0` + > This option can be set to override the default value of 1.0 for the slope \f$ \gamma \f$ of the function \f$ f(p) \f$ for \f$ p \ge 0 \f$. For a + > larger value the step size will depend more strongly on the difference between the likelihood of the walker and the current mean likelihood. + + The parallel-tempering algorithm runs MCMC chains at different temperatures and can swap the temperature of two walkers if one of them + has found a location with a good likelihood. In this way the hot walkers can explore a large part of the parameter space quickly, while + cold walkers explore the regions with a high likelihood more thoroughly. At the end of the run, the cold walkers with \f$ T=1 \f$ sample + the posterior distribution. A fraction of the walkers is set to \f$ T=1 \f$, while the other walkers follow a logarithmic temperature + distribution with a maximum temperature \f$ T_\mathrm{max} \f$ that can be specified in the parameter file.\n\n + + `PT_COLD_FRACTION=0.25` + > This option can be set to override the default 25% fraction of cold walkers with \f$ T=1 \f$. + + `PT_TARGET_ACCEPTANCE=0.3` + > The step size for new walker postions is adjusted on the fly, such that the resulting acceptance fraction has a specified value. + > With this option the default value for the desired acceptance fraction of 30% can be overridden. + + `PT_STEPS_SCALE_ADJUST=10` + > The step size for new walker postions is adjusted is adjusted every 10 steps by default. This option can be set to override this choice. + + + \section config_output Output options + + `HDF5_SUPPORT` + > This option enables support for the HDF5 file format. Note that it can only be used, if the HDF5 library is installed and properly + > linked in the `Makefile`. If this option is set, the desired output file format can be specified in the parameter file. Otherwise + > all output files will bin in ASCII fromat. + + `WRITE_GALAXY_CATALOG` + > If this option is set, Emerge writes galaxy catalogues to the `output` folder. The output redshifts, stellar mass threshold, number + > of files per redshift, and output format can be specified in the parameter file for each run. For details on the structure of the + > galaxy catalogues see \ref output_galaxy_cat. + + `WRITE_HALO_CATALOG` + > If this option is set, Emerge writes dark matter halo catalogues to the `output` folder for the same redshifts, stellar mass threshold, + > number of files per redshift, and output format as the galaxy catalogues. For details on the structure of the halo catalogues see + > \ref output_halo_cat. + + `WRITE_MAINBRANCH` + > If this option is set, Emerge writes the main branch, i.e. only main progenitors and descendants for seleced systems to the + > `output` folder. These galaxies are seleced at a specific redshift and a stellar or halo mass bin in the parameter file.\n + + + \page parameterfile Parameter file + + Many aspects of Emerge do not need to be set before compilation, but can be specified at run-time in a parameter file whenever + the code is started. Each parameter is set by first specifying a keyword and then a numerical value or a character string, separated + by spaces or tabs. A separate line has to be used for each keyword, and the corresponding value has to appear in the same line. + It is possible to use an arbitrary amount of space between keywords, including empty lines. The keywords can appear in any order, + but can only appear once. If a necessary keyword has been omitted, or a keyword that is not needed has been set, the code will + print an error message, and the parameter file has to be adjusted. All keywords are type-sensitive. Empty lines, or lines with + a leading `%` are ignored. Comments can be added after the value for the corresponding keyword. + + This section discusses the parameters in detail. All keywords marked with an asterisk will be ignored if Emerge is restarting + a fitting algorithm, as these values are loaded from the restart file. In this case it is typically not needed to change the + other parameters. + + + \section parameters_setup General Setup + + `ModelName`       `P100` + > This is the name of the model. A folder with the same name will be created in the `output` folder, and all output + > files will be written to this folder. + + `UniversesInParallel`       `1` + > This specifies the number of universes, i.e. walkers, that will be computed in parallel. If more than one universe are + > computed in parallel, the initial setup of the halo merger trees will be done for the first universe and then duplicated + > for the other universes. For a simple generation of mock catalogues, this parameter can be set to `1`. For parameter + > space exploration, ideally the number of computing cores per universe, i.e. the total number of available cores divided + > by the number of parallel universes, is chosen such that it fits in the available memory per core. This setup results in + > as many parallel universes as possible and will typically lead to the best performance. For example, if 400 cores are + > available, and at least 10 cores are needed to hold one universe, `UniversesInParallel` should be set to 40. Note that + > the number of cores must be a multiple of `UniversesInParallel`. + + `NumFilesInParallel`       `4` + > The code can read halo merger tree files and write output files in parallel. This parameter controls how many files are + > processed in parallel, and is ideally set to a subdivision of the number of halo merger tree files. + + `Verbose`       `1` + > This parameter regulates how much screen output is produced. Currently it can be set to 1 (minimal screen output), + > and 2 (maximal screen output). + + `MaxMemSize`       `1500` + > This parameter specifies the amount of memory in MByte that the dynamic memory manager allocates for each computing + > core. This should typically be set to the amount of available memory divided by the number of cores. If `MaxMemSize` has been + > chosen too low, the code will print an error message and inform how much memory is needed for the specified setup. + > In case the available memory per core is lower than this value, either the number of parallel universes must be decreased, + > or more memory is needed, i.e. additional computing nodes must be added. + + `BufferSize`       `100` + > This parameter specifies the size in Mbyte for the multi-purpose communication buffer used by the code in various parts of the + > parallel algorithms. It should be large enough to have minimal work-load imbalance losses, though in practice a few to 100 MBytes + > are sufficient. + + + \section parameters_files Input files + + `TreefileName`       `trees/P100` + > This sets the filename of the halo merger tree files to be read in at start-up. The trees can be distributed into several files. + > In this case, only the base name without the tailing .n number should be specified. The code will recognise the number of files + > that contain the halo merger trees automatically, and load all of these files accordingly. In the example above, the code will + > read the files `P100.0`, `P100.1`, and so on, in the `trees` folder. + + `SMFfileName`       `data/smf.dat` + > This sets the location and the name of the file that contains the observed stellar mass functions. For details on the structure + > of this file, see \ref data_smf. + + `FQfileName`       `data/fq.dat` + > This sets the location and the name of the file that contains the observed fractions of quenched galaxies as function of stellar mass + > and redshift. For details on the structure of this file, see \ref data_fq. + + `CSFRDfileName`       `data/csfrd.dat` + > This sets the location and the name of the file that contains the observed cosmic star formation rate densities. + > For details on the structure of this file, see \ref data_csfrd. + + `SSFRfileName`       `data/ssfr.dat` + > This sets the location and the name of the file that contains the observed specific star formation rates as function of stellar mass + > and redshift. For details on the structure of this file, see \ref data_ssfr. + + `WPfileName`       `data/wp.dat` + > This sets the location and the name of the file that contains the observed projected auto-correlation functions. For details on the + > structure of this file, see \ref data_wp. + + + \section parameters_cosmo Cosmological parameters + + These parameters specify the cosmology and have to take the same values that have been used to simulated the halo merger trees.\n\n\n + + `HubbleParam`       `0.6781` + > This sets the value of the Hubble constant in units of 100 kms -1 Mpc -1. + + `Omega0`       `0.3080` + > This sets the value of the cosmological matter density parameter in units of the critical density at \f$ z=0 \f$. + + `OmegaLambda`       `0.6781` + > This sets the value of the cosmological dark energy density parameter for a cosmological constant in units of the critical + > density at \f$ z=0 \f$. A geometrically flat universe has `Omega0 + OmegaLambda = 1`. + + `OmegaBaryon`       `0.0484` + > This sets the value of the cosmological baryon density parameter in units of the critical density at \f$ z=0 \f$. The resulting + > value for universal baryon fraction is given by `Omega0 / OmegaLambda`. + + + \section parameters_sim Simulation parameters + + `BoxSize`       `67.81` + > This parameter specifies the side length of the simulation box from which the dark matter halo merger trees have been extracted. + > Note that it should have the same unit as the tree files, e.g. if the side length is 100 Mpc, but the unit used for the merger + > trees is Mpc/h, the value given here should be `67.81` (for \f$ h = 0.6781 \f$). + + + \section parameters_units Units + + These parameters set the internal units. All simulated and observed data that is read in will be converted to these internal units. + The output data will be converted to convenient units (see \ref outputfiles for more details). + + `UnitLength_in_Mpc`       `1.0` + > This sets the internal length unit in Mpc. + + `UnitTime_in_yr`       `1.0e9` + > This sets the internal time unit in years. The above choice is convenient for cosmological boxes as it sets the time unit to one Gyr. + + `UnitMass_in_Msun`       `1.0e9` + > This sets the internal mass unit in solar masses. The above choice is convenient, as the interal unit for the star formation rate + > is then one solar mass per year. + + + \section parameters_sfe Conversion efficiency parameters + + These parameters regulate the instantaneous baryon conversion efficiency. If Emerge is run with a mode for which only one model is + computed, the exact values specified are used. If Emerge is run with a mode for which walkers with random initial positions are + evolved, the values specify the mean of the bin from which the inital values for the walkers are sampled. + + `Eff_MassPeak`       `11.35` + > This sets the \f$ z=0 \f$ value \f$ M_0 \f$ for the parameter \f$ M_1 \f$ of the conversion efficiency, which specifies the halo mass + > close to which the conversion efficiency peaks. + + `Eff_Normalisation`       `0.009` + > This sets the \f$ z=0 \f$ value \f$ \epsilon_0 \f$ for the parameter \f$ \epsilon_\mathrm{N} \f$ of the conversion efficiency, + > which specifies its normalisation. + + `Eff_LowMassSlope`       `3.09` + > This sets the \f$ z=0 \f$ value \f$ \beta_0 \f$ for the parameter \f$ \beta \f$ of the conversion efficiency, which specifies its + > low-mass slope. + + `Eff_HighMassSlope`       `1.11` + > This sets the \f$ z=0 \f$ value \f$ \gamma_0 \f$ for the parameter \f$ \gamma \f$ of the conversion efficiency, which specifies its + > high-mass slope. This parameter is not present, if the code has been compiled with the option `SFE_SAME_SLOPE`. + + `Eff_MassPeak_Z`       `0.65` + > This sets the redshift scaling \f$ M_\mathrm{z} \f$ for the parameter \f$ M_1 \f$ of the conversion efficiency. This parameter is + > only present if the code has been compiled with the option `SFE_MPEAK_ZEVOLV`. + + `Eff_Normalisation_Z`       `0.60` + > This sets the redshift scaling \f$ \epsilon_\mathrm{z} \f$ for the parameter \f$ \epsilon_\mathrm{N} \f$ of the conversion efficiency. + > This parameter is only present if the code has been compiled with the option `SFE_NORM_ZEVOLV`. + + `Eff_LowMassSlope_Z`       `-2.02` + > This sets the redshift scaling \f$ \beta_\mathrm{z} \f$ for the parameter \f$ \beta \f$ of the conversion efficiency. This parameter + > is only present if the code has been compiled with the option `SFE_BETA_ZEVOLV`. + + `Eff_HighMassSlope_Z`       `0.0` + > This sets the redshift scaling \f$ \gamma_\mathrm{z} \f$ for the parameter \f$ \gamma \f$ of the conversion efficiency. This parameter + > is only present if the code has been compiled with the option `SFE_GAMMA_ZEVOLV`, and the option `SFE_SAME_SLOPE` has not been set. + + + \section parameters_sats Satellite galaxy parameters + + These parameters regulate the evolution of satellite galaxies. If Emerge is run with a mode for which only one model is + computed, the exact values specified are used. If Emerge is run with a mode for which walkers with random initial positions are + evolved, the values specify the mean of the bin from which the inital values for the walkers are sampled. + + `Fraction_Escape_ICM`       `0.56` + > This sets the escape fraction for galaxy mergers, i.e. the fraction of the stellar mass of the satellite galaxy that gets ejected + > into the ICM during a galaxy merger. + + `Fraction_Stripping`       `0.004` + > This parameter defines the threshold for the subhalo mass below which satellites are instantly stripped to the ICM. If the option + > `SAT_STRIP_USE_MSTAR` is set, satellite galaxies are stripped if the mass of their subhalo drops below `Fraction_Stripping` times + > their own stellar mass, otherwise they are stripped if the mass of their subhalo drops below `Fraction_Stripping` times it maximum + > mass through history. + + `Timescale_Quenching`       `4.46` + > This sets the timescale for satellite quenching. If a subhalo has dropped below its maximum mass through history for more than + > `Timescale_Quenching` times the halo dynamical time, the SFR in the satellite galaxy is set to zero. + + `Timescale_Quenching`       `0.35` + > If the option `SAT_QUENCH_MASS_DEPENDENT` has been set, this parameter defines the slope \f$ \tau_\mathrm{s} \f$ of the mass + > dependence of the quenching timescale + > \f[ \tau = \tau_0 \cdot t_\mathrm{dyn} \cdot \mathrm{max} [(m_* / 10^{10}\mathrm{M}_\odot)^{-\tau_\mathrm{s}},1] \;.\f] + + `Decay_Quenching`       `0.0` + > If the option `SAT_SFR_EXP_DECAY` has been set, this parameter sets the timescale for the exponential decay of the SFR after the + > subhalo falls below its maximum mass. After the quenching timescale \f$ \tau \f$ has elapsed, the SFR is still set to zero. + + + \section parameters_adjust Adjustable parameters + + `Mstar_min`       `7.0` + > Minimum logarithmic stellar mass in solar masses that is used to compute global statistics such as stellar mass functions, quenched + > fractions and specific star formation rates. All observational data below this mass are ignored in the fit. + + `Mstar_max`       `12.5` + > Maximum logarithmic stellar mass in solar masses that is used to compute global statistics such as stellar mass functions, quenched + > fractions and specific star formation rates. All observational data above this mass are ignored in the fit. + + `Delta_Mstar`       `0.2` + > Stellar mass bin size in dex used to compute global statistics. During the fitting process the model values corresponding to individual + > oberved data will be interpolated from these bins. + + `Observation_Error_0`       `0.08` + > In Emerge, each galaxy has an *intrinsic* stellar mass, i.e. the physically self-consistent stellar mass given by the integral of past + > star formation and mass loss, and an *observed* stellar mass that is computed from the intrinsic mass and a gaussian distribution for + > the observational error. The standard deviation of this distribution linearly depends on redshift as + > \f$ \sigma = \sigma_0+\sigma_\mathrm{z}\cdot z \f$. The \f$ z=0 \f$ scatter \f$ \sigma_0 \f$ is set by `Observation_Error_0`. + + `Observation_Error_z`       `0.06` + > The scatter between *intrinsic* and *observed* stellar mass increases linearly with redshift. The slope \f$ \sigma_\mathrm{z} \f$ + > is set by `Observation_Error_z`. The scatter increases only up to redshift of \f$ z=4 \f$, after which it is held constant. This + > choice can be changed by modifying the value of `ZMAX_SMFERR` in the `allvals.h` file. + + `Global_Sigma_SMF_LZ`       `0.15` + > If the option `GLOBAL_SIGMA_SMF` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the stellar mass function below \f$ z=0.1 \f$. This redshift threshold can be changed by modifying the value of `SIGMASMFZTHRESH` in + > the `allvals.h` file. This increased uncertainty can be sensible if different selected observational data sets are not in agreement with + > each other given their original uncertainties. + + `Global_Sigma_SMF_HZ`       `0.30` + > If the option `GLOBAL_SIGMA_SMF` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the stellar mass function above \f$ z=0.1 \f$. + + `Global_Sigma_FQ`       `0.10` + > If the option `GLOBAL_SIGMA_FQ` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the quenched fractions. + + `GLOBAL_SIGMA_CSFRD`       `0.10` + > If the option `GLOBAL_SIGMA_CSFRD` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the cosmic SFR density. + + `GLOBAL_SIGMA_SSFR`       `0.15` + > If the option `GLOBAL_SIGMA_SSFR` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the specific SFRs. + + `GLOBAL_SIGMA_WP`       `0.40` + > If the option `GLOBAL_SIGMA_WP` has been set, this parameter is added in quadrature to the observational uncertainty of each data point + > of the projected galaxy auto-correlation functions. + + + \section parameters_fit Fitting parameters + + When the model parameters are fit using any of the fitting algorithms (code modes 1, 2, or 3), the walkers are initialised uniformly in a + hyper-cube centred around the values given above. The width of the cube in each dimension is given by the following parameters, which have + the same names as the fitted parameters plus an extension of `_Range`. For example, if the fitted parameter has a value of 1.0 and the + `_Range` parameter has a value of 0.5, the walkers will be initialised uniformly between 0.75 and 1.25 in this dimension. Note that some + parameters are fit in logarithmic space, as indicated below. + + `Eff_MassPeak_Range`       `0.2` + > Range of the initial distribution of the parameter `Eff_MassPeak`. + + `Eff_Normalisation_Range`       `0.5` + > Range of the initial distribution of the parameter `Eff_Normalisation`. This parameter is fit in logarithmic space, and the range is + > given in dex. For example, if `Eff_Normalisation` is 0.01 and `Eff_Normalisation_Range` is 0.5, the walkers will be distributed + > logarithmically between \f$ 10^{[\log_{10}(0.01) - 0.5/2]} = 0.0056 \f$ and \f$ 10^{[\log_{10}(0.01) + 0.5/2]} = 0.0178 \f$. + + `Eff_LowMassSlope_Range`       `0.5` + > Range of the initial distribution of the parameter `Eff_LowMassSlope`. + + `Eff_HighMassSlope_Range`       `0.2` + > Range of the initial distribution of the parameter `Eff_HighMassSlope`. + + `Eff_MassPeak_Z_Range`       `0.2` + > Range of the initial distribution of the parameter `Eff_MassPeak_Z`. + + `Eff_Normalisation_Z_Range`       `0.2` + > Range of the initial distribution of the parameter `Eff_Normalisation_Z`. + + `Eff_LowMassSlope_Z_Range`       `0.5` + > Range of the initial distribution of the parameter `Eff_LowMassSlope_Z`. + + `Eff_HighMassSlope_Z_Range`       `0.2` + > Range of the initial distribution of the parameter `Eff_HighMassSlope_Z`. + + `Fraction_Escape_ICM_Range`       `0.3` + > Range of the initial distribution of the parameter `Fraction_Escape_ICM` in dex, distributed logarithmically. + + `Fraction_Stripping_Range`       `0.3` + > Range of the initial distribution of the parameter `Fraction_Stripping` in dex, distributed logarithmically. + + `Timescale_Quenching_Range`       `0.5` + > Range of the initial distribution of the parameter `Timescale_Quenching` in dex, distributed logarithmically. + + `Slope_Quenching_Range`       `0.4` + > Range of the initial distribution of the parameter `Slope_Quenching`. + + `Decay_Quenching_Range`       `0.2` + > Range of the initial distribution of the parameter `Decay_Quenching`. + + + \section parameters_mcmc MCMC parameters + + For parameter space exploration, three different code modes can be used. The following hyperparameters regulate these algorithms. + + `NumberOfMCMCWalkers`       `8` + > This sets the number of walkers used for all parameter space exploration methods. Each walker then moves through parameter space + > for each step of the algorithm, forming a chain. As all parameter space exploration algorithms are ensemble methods, a large + > number here is recommended, ideally several hundreds of walkers. + + `MCMCScaleParameter`       `2.0` + > This sets the step size with which new walker positions will be suggested. For the affine-invariant MCMC sampler (code mode 1), + > `MCMCScaleParameter` defines the \f$ a \f$ parameter and is suggested to be chosen around 2. For the HYBRID method + > (code mode 2) and the parallel tempering algorithm (code mode 3), this parameter sets the initial gaussian trial step, based on the + > parameter range divided by two to the power of `MCMCScaleParameter`. For example, if the parameter range `Eff_MassPeak_Range` is + > set to 0.2, and `MCMCScaleParameter` is set to 4, a new trial step size will be drawn from a gaussian distribution with a sigma + > of \f$ 0.2 / 2^4 = 0.0125 \f$ for the dimension corresponding to the parameter `Eff_MassPeak`. A higher value leads to + > a larger step size, and consequently to a lower acceptance rate. For code modes 1 and 2 this parameter can be adjusted to obtain + > the desired acceptance rate, while for code mode 3, the step size is adjusted on the fly to get an acceptance rate as defined by + > the option `PT_TARGET_ACCEPTANCE`. + + `ChainTemperature`       `100.0` + > This sets the initial temperature for the chains. If the code is run with mode 1 (MCMC), this parameter is ignored. If the code is + > run with mode 2 (HYBRID), `ChainTemperature` sets the initial temperature of all chains. For a positive value, this temperature will + > decrease on a logarithmic timescale. For a negative value, the temperature will be held constant at the absolute value of + > `ChainTemperature`. If the code is run with mode 3 (parallel tempering), `ChainTemperature` sets the temperature of the hottest chain. + > A fraction of the walkers (set with the option `PT_COLD_FRACTION`) is initialised at a temperature of \f$ T=1 \f$, while the remaining + > walkers will have temperatures spaced logarithmically up to the value of `ChainTemperature`. + + `TimeLimit`       `180.0` + > This sets the time limit of the fitting procedure in seconds. The algorithm is stopped, if the next chain would be completed after + > the time limit. This parameter should be adapted to the computing time limit of the system Emerge is running on, taking into account + > the time the code needs to set up everything before the fitting starts. In this way, the code can be terminated before the load + > managing system stops it while running. + + `MCMCseed`       `12345` + > This integer value sets the seed for the fitting algorithm. For the same seed value, the algorithm will produce the same walker chains. + > If the code is run to start a new fitting procedure, a file containing the seed value and a `000` ending will be created, e.g. + > `mcmc12345.000.out`. If this file already exists, it will be overridden. If fitting is resumed with `1` added to the flag behind the + > parameter file (e.g. 11 to resume the MCMC fitting), a new file will be created with an incremented number. For example, if the fitting + > is resumed for the first time, the file `mcmc12345.001.out`, will be created. If the fitting is restarted again, the file + > `mcmc12345.002.out` will be created, and so on. Changing the seed value will result in completely different chains, such that several + > ensembles of walkers with different seeds can be run at the same time. + + + \section parameters_output Output parameters + + `NumOutputFiles`       `1` + > This sets the number of files each output product is split into. This applies to galaxy and catalogues at a given redshift, while + > catalogues at different redshifts will be already separated into different files. + + `OutputFormat`       `1` + > This parameter indicates which output format will be used. A value of 1 will produce output in ASCII format, while for a value of + > 2, the HDF5 format is used. For more details on the specific structure of the output files, see \ref outputfiles. + + `OutputRedshifts`       `0.1,0.5,1.0` + > This comma-separated list specifies the output redshifts for which galaxy and halo catalogues are created. If only a single value + > is given, only one output catalogue at this redshift will be written. If several values are given, separated by commas, output files + > will be written for each redshift. Note that there must not be any space between values and commas. + + `OutputMassThreshold`       `7.0` + > This sets the minimum stellar mass for which a system is included in the output catalogues. The value is given in logarithmic solar + > masses. + + `MainBranchMasses`       `10.0,11.0,12.0` + > This sets the mean of the mass bin for which galaxy main branches will be written. The parameter `MainBranchMassType` controls if + > these are given in halo masses or stellar masses. If only a single value is given, only one mass bin will be written. If several + > values are given, separated by commas, each mass bin will be written. Note that there must not be any space between values and commas. + + `MainBranchBinSize`       `0.1,0.2,0.5` + > This sets the mass bin width for which main branches will be written. The number of values given in this list must be the same as + > for the `MainBranchMasses` list. + + `MainBranchMassType`       `1` + > This defines which mass will be used for the selection. For a `0` the mass bin corresponds to the halo mass, while for `1` the mass + > bin corresponds to the (intrinsic) stellar mass. + + `MainBranchRedshift`       `0.0` + > This value sets the redshift at which the systems are selected, i.e. they must have a (halo or stellar) mass that falls into the + > specified bins at this redshift. + + + \page datafiles Observed data files + + In Emerge the properties of simulated dark matter haloes are linked to the observed properties of galaxies. This is achieved by + using global statistics, such as the stellar mass function and galaxy clustering. Each time a model with a set of parameters is + run, mock observations are computed and compared to observed data. This data is stored in ASCII format in several files in the + `data` folder. To fit the model to different data sets, these files can be simply modified to suit any configuration. Typically, + they contain blocks of individual data points representing data sets reported by different publications. The following + sections provide an overview of the different statistical data, the format of the files, and how to modify them. + + + \section data_smf Stellar Mass Functions + + The stellar mass functions are stored in the `data/smf.dat` file, and are read and used in the fit only if the option + `READ_SMF` has been set. This file starts with a general header line and is followed by several blocks of data, which in turn + consist of a header line and individual data. The general header starts with a `#` and then lists the entries that each data block + header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data points in + this block, the minimum and the maximum redshift of the stellar mass function, a correction value for the (universal) initial mass + function (IMF) of the data, the Hubble parameter, and a string that identifies the publication from which the data is taken. A typical + block header thus has the format\n\n + `# 39 0.0 0.2 0.0 1.0 Li & White 2009`,\n\n + which signifies that the data block has 39 entries (excluding this header), ranges from \f$ z=0.0 \f$ to \f$ z=0.2 \f$, has no IMF + correction, uses a Hubble parameter of \f$ h=1.0 \f$, and is from the paper by Li & White (2009). + + The IMF can be normalised to any choice, as long as this choice is consistent for all data files. For example, if the model shall be + calibrated to a Chabrier IMF, the correction for the data blocks that are given for a Chabrier IMF is 0, while for the data blocks that + are given for a Salpeter IMF, the correction value would be 0.24, which is then subtracted from the logarithmic stellar masses. The + block header will then look like `# 19 0.6 1.0 0.24 0.7 Santini 2012`. If instead the masses shall be calibrated to a Salpeter IMF, + data using a Chabrier IMF will need to have a block header with a IMF correction value of -0.24, while data using a Salpeter IMF would + have a correction value of 0. + + Each data block consists of several lines that form the stellar mass function in the given redshift interval. These lines consist of + the logarithmic stellar mass, the logarithm of the number density per dex, the upper \f$ 1 \sigma \f$ number density, the lower + \f$ 1 \sigma \f$ number density, and a weight factor. This factor will be multiplied to the \f$ \chi^2 \f$ value of the corresponding + data point. For a complete parameter space exploration it should be set to 1 for each data point. However, for testing purposes it can + be set to zero to ignore some data, or to a larger value to see how the model changes if one observation is emphasised. A typical data + block will look like:\n\n + `# 39 0.0 0.2 0.0 1.0 Li & White 2009`\n + `8.05 -1.216 -1.174 -1.263 1`\n + `8.15 -1.312 -1.264 -1.366 1`\n + `... [35 more lines] ...`\n + `11.45 -4.745 -4.702 -4.794 1`\n + `11.55 -5.312 -5.237 -5.403 1`\n\n + New data blocks can be simply added to the end of the file, taking into account that each block needs a header as described above, and + that the number of data points in the header correspond to the number of lines in that block. Note that there must not be empty lines + before the end of the file. + + + \section data_fq Quenched Fractions + + The fraction of quenched galaxies as a function of stellar mass are stored in the `data/fq.dat` file, and are read and used in the fit + only if the option `READ_FQ` has been set. This file starts with a general header line and is followed by several blocks of data, which + in turn consist of a header line and individual data. The headers have the same format as in the stellar mass function file. The IMF + can be normalised with a correction value in the same way as for the stellar mass functions. Note that the IMF choice has to be the same + everywhere. + + The data blocks consist of several lines that give the quenched fractions in the given redshift interval. These lines consist of + the logarithmic stellar mass, the fraction of quenched galaxies, the \f$ 1 \sigma \f$ uncertainty, and a weight factor that will be + multiplied to the \f$ \chi^2 \f$ value of the corresponding data point. A typical data block will look like:\n\n + `# 32 0.2 0.5 0.03 0.7 Muzzin 2013`\n + `8.4 0.142 0.157 1`\n + `8.5 0.122 0.100 1`\n + `... [28 more lines] ...`\n + `11.4 0.943 0.237 1`\n + `11.5 0.920 0.285 1`\n\n + New data blocks can be added to the end of the file in the same way as for the SMFs. + + + \section data_csfrd Cosmic Star Formation Rate Density + + The cosmic star formation rate densities are stored in the `data/csfrd.dat` file, and are read and used in the fit only if the option + `READ_CSFRD` has been set. This file starts with a general header line and is followed by several blocks of data, which + in turn consist of a header line and individual data. The general header starts with a `#` and then lists the entries that each data block + header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data points in + this block, the IMF correction value, the Hubble parameter, and a string that identifies the publication from which the data is taken + and the wavelength band that has been used. A typical block header thus has the format\n\n + `# 1 0.24 1.0 Robotham & Driver 2011 UV`,\n\n + which signifies that the data block has 1 entry, wil be converted from a Salpeter to a Chabrier IMF, uses a Hubble parameter + of \f$ h=1.0 \f$, and is from the paper by Robotham & Driver (2011). Note that the IMF choice has to be the same everywhere. + + The data blocks consist of several lines that give the cosmic star formation rate density at several redshifts. These lines consist of + the redshift, the logarithmic cosmic star formation rate density in solar masses per year and cubic Megaparsec, the + upper \f$ 1 \sigma \f$ uncertainty in dex, the lower \f$ 1 \sigma \f$ uncertainty in dex, and a weight factor that will be + multiplied to the \f$ \chi^2 \f$ value of the corresponding data point. A typical data block will look like:\n\n + `# 4 0.0 0.7 Zheng 2006 UV/IR`\n + `0.3 -1.7060 0.1261 0.1821 1`\n + `0.5 -1.4575 0.0977 0.1255 1`\n + `0.7 -1.2438 0.0710 0.0801 1`\n + `0.9 -1.2103 0.0776 0.0944 1`\n\n + New data blocks can be simply added to the end of the file, taking into account that each block needs a header as described above, the + number of data points in the header corresponds to the number of lines in that block, and there are no empty lines + before the end of the file. + + + \section data_ssfr Specific Star Formation Rates + + The specific star formation rates are stored in the `data/ssfr.dat` file, and are read and used in the fit only if the option + `READ_SSFR` has been set. This file starts with a general header line and is followed by several blocks of data, which + in turn consist of a header line and individual data. The headers have the same format as in the cosmic star formation rate density + file. The IMF can be normalised with a correction value, but the IMF choice has to be the same everywhere. + + The data blocks consist of several lines that give the specific star formation rate as a function of stellar mass and redshift. + These lines consist of the redshift, logarithmic stellar mass, the logarithm of the specific star formation rate in \f$ yr^{-1} \f$, + the upper \f$ 1 \sigma \f$ uncertainty in dex, the lower \f$ 1 \sigma \f$ uncertainty in dex, and a weight factor that will be + multiplied to the \f$ \chi^2 \f$ value of the corresponding data point. A typical data block will look like:\n\n + `# 60 0.0 0.7 Whitaker 2012`\n + `0.25 8.58 -9.31 0.3 0.3 1`\n + `0.25 8.69 -9.29 0.3 0.3 1`\n + `... [56 more lines] ...`\n + `2.25 11.00 -8.78 0.3 0.3 1`\n + `2.25 11.25 -8.90 0.3 0.3 1`\n\n + New data blocks can be simply be added to the end of the file, although there must not be empty lines before the end of the file. + + + \section data_wp Projected correlation functions + + The projected galaxy auto-correltion function are stored in the `data/wp.dat` file, and are read and used in the fit only if the option + `READ_WP` has been set. In the current implementation, all observed correlation functions must be at the same redshift, which is + specified at the beginning of the file. The following line is a general header that starts with a `#` and then lists the entries that + each data block header needs to consist of. These are a `#`, which identifies the line as a block header, the number of individual data + points in this block, the minimum logarithmic stellar mass for this bock, the maximum logarithmic stellar mass for this block, the + maximum line-of-sight projection distance \f$ \pi_\mathrm{max} \f$, the IMF correction value, the Hubble parameter, and a string that + identifies the publication from which the data is taken. A typical block header thus has the format\n\n + `# 25 9.0 9.5 10.0 0.0 1.0 Li 2006`,\n\n + which signifies that the data block has 25 entries (excluding this header), ranges from \f$ \log m_* = 9.0 \f$ to \f$ \log m_* = 9.5 \f$, + has a projection distance of \f$ \pi_\mathrm{max} = 10 \mathrm{Mpc}/h \f$, no IMF correction, uses a Hubble parameter of \f$ h=1.0 \f$, + and is from the paper by Li et al. (2006). + + Each data block consists of several lines that give the projected correlation function in the given stellar mass bin. These lines consist + of the projected distance in \f$ \mathrm{Mpc}/h \f$, the projected correlation function in \f$ \mathrm{Mpc}/h \f$, the \f$ 1 \sigma \f$ + uncertainty, and a weight factor that will be multiplied to the \f$ \chi^2 \f$ value of the corresponding data point. A typical data block + will look like:\n\n + `# 25 9.0 9.5 10.0 0.0 1.0 Li 2006`\n + `0.11 411.3 133.7 1`\n + `0.14 428.6 94.3 1`\n + `... [21 more lines] ...`\n + `22.54 0.9 1.8 1`\n + `28.37 0.5 1.8 1`\n\n + New data blocks can be simply be added to the end of the file. Note that there must not be empty lines before the end of the file, and + the redshift for all correlation functions must be the same (given at the start of the file). + + + \page treefiles Halo merger tree files + + In addition to observed galaxy data, Emerge relies on simulated dark matter halo merger trees that will be populated with galaxies. The + files that containt these merger trees and their locations can be specified in the parameter file with the parameter `TreefileName`. The + suggested location for the merger tree files is the `trees` folder. If the trees are located in a single file, `TreefileName` corresponds + to the name of this file including folder, e.g. `trees/P100`. If the trees are stored within multiple files, `TreefileName` gives the file + base name, which will be appended with the file index, e.g. `trees/P100` corresponds to the files `P100.0`, `P100.1`, `P100.2`, `...`, + in the `trees` folder. There is no restriction on the number of files that the trees are split into, although an individual tree cannot + be split into different files. The code will automatically detect the number of files. + Merger tree files extracted from a simulation with a side length of 100 Mpc can be downloaded from the code website + http://www.usm.lmu.de/emerge. + + + + Each merger tree file has the same binary format. It starts with header giving the basic information contained in the file, which is + followed by the properties for each halo. The files use the common `unformatted binary' convention, for which the data of each read or + write statement is bracketed by fields that give the length of the data block in bytes. One such 4-byte integer field is stored before and + after each data block. This is useful to check the consistency of the file structure, to make sure that the data is read at the correct + place, and to skip individual blocks quickly by using the length information of the block size fields to fast forward in the file without + actually having to read the data. The sequence of blocks in a file has the following format: + +  #  | Block name | Type | Bit size | Elements | Comment + :-----------: | ---------- | ------ | :--------: | :---------: | -------- + 0 | `Ntrees` | int | 32 | 1 | Number of merger trees in this file + 1 | `Nhalos` | int | 32 | `Ntrees` | Number of haloes in each merger tree + 2 | `TreeID` | IDtype | 32 or 64 | `Ntrees` | ID of each tree + 3 | `Halo` | struct | 480 or 576 | `Nhalostot` | Properties of each halo in this file + + The data type `IDtype` is `unsigned int` by default. However, for very large simulations the total number of haloes can be larger than + 2 billion. In this case `IDtype` needs to be able to cover this range, and can be set to the type `unsigned long long int` if the `LONGIDS` + option is enabled. In total the `Halo` block contains `Nhalostot = Sum_i Nhalos_i` elements, i.e. the sum of the haloes in all trees. + The halo properties are stored in the form of a structure for each halo. This structure has the following format: + +  #  | Block name | Type | Bit size | Example | Comment + :-----------: | ---------- | -------------- | :--------: | ------------- | -------- + 0 | `haloid` | IDtype | 32 or 64 | 123456788 | ID of the halo (unique across simulation and snapshots) + 1 | `descid` | IDtype | 32 or 64 | 123456789 | ID of the halo's descendant in the next snapshot + 2 | `upid` | IDtype | 32 or 64 | 223456788 | ID of the most massive host halo + 3 | `np` | unsigned short | 16 | 5 | Number of progenitors in the previous snapshot + 4 | `mmp` | unsigned short | 16 | 1 | Flag indicating if the halo is the most massive progenitor (1) or not (0) + 5 | `scale` | float | 32 | 0.99 | Scale factor \f$ a \f$ of the halo + 6 | `mvir` | float | 32 | 1.1e12 | Virial mass in \f$ \mathrm{M}_\odot/h \f$ + 7 | `rvir` | float | 32 | 220.5 | Virial radius in \f$ \mathrm{kpc}/h \f$ + 8 | `c` | float | 32 | 11.5 | Halo concentration (\f$ R_\mathrm{vir}/R_\mathrm{s} \f$) + 9 | `lambda` | float | 32 | 0.033 | Halo spin parameter + 10 | `pos[3]` | float (x3) | 96 | 3.1, 2.4, 7.1 | Halo position in \f$ \mathrm{Mpc}/h \f$ \f$ (x,y,z) \f$ + 11 | `vel[3]` | float (x3) | 96 | 302, -19, 212 | Halo velocity in \f$ \mathrm{km/s} \f$ \f$ (v_x,v_y,v_z) \f$ + + The halo IDs may not be smaller than 1. If a halo has no descendant (e.g. for \f$ z=0 \f$), the descendant ID `descid` should be 0. Similarly, + if a halo is a host halo, the ID of the most massive host halo `upid` should be 0. Both the position and the velocity are stored as + 3D arrays. + + For some applications, for example to compute the amount of stellar mass in the ICM, it is necessary that the trees are ordered within + forests, such that all merger trees that reference the same host ID need to be grouped together. In practice, this is achieved by providing + a separate file in the same folder as the tree files, which lists for each tree ID (left column) the corresponding forest ID (right column). + The name of the file has to consist of the tree file base name and a `.forests` extension, e.g. `P100.forests`. + + While merger trees that have been created with any software can easily be converted to this format, a code that converts merger trees created + with the Rockstar / consistent-trees software into the Emerge format is provided in the `tools` folder. Compiling Emerge should automatically + also compile the `convert_CT_to_emerge.c` file and create the `convert_CT_to_emerge.c` executable, which converts a single ASCII tree file + created with consistent-trees. Before compiling, it should be verified that the column numbers specified at the beginning of this file correspond + to the correct halo properties for the adopted version of consistent-trees. The executable must be called with the input file and output file + as arguments. If consistent-trees has divided the merger trees into several files (boxes) with the names `tree_0_0_0.dat`, `tree_0_0_1.dat`, + `...`, the shell script `consistent_trees_to_emerge.sh` can convert all files to the appropriate Emerge files. It needs to be called with the + output base name and the number of box divisions as arguments. The command `consistent_trees_to_emerge.sh P100 2` converts all 8 files from + `tree_0_0_0.dat` to `tree_1_1_1.dat` into the files `P100.0` to `P100.7`. + + + \page outputfiles Output files + + At the end of each run, Emerge can write different data products to files. This includes the model predictions for the statistics that + have been used in the fit, mock galaxy catalogues, halo catalogues, and the main branch evolution of each system. In future releases, + Emerge will also be able to write the full galaxy merger trees, and support lightcone output. All files can either be written in standard + ASCII format, or, if the option `HDF5_SUPPORT` has been set and the proper libraries have been installed, the HDF5 format can be used. + The output format can be selected before each run in the parameter file with the `OutputFormat` parameter. All files will be written to + the `output` folder, within the subfolder corresponding to the parameter `ModelName`. Emerge supports parallel output by distributing + the catalogues into several files, each written by a group of processors, which may simplify the handling of very large simulations. + + + \section output_galaxy_stats Global statistics + + The global statistics that have been used to fit the model will be written to individual files in the `statistics` subfolder for ASCII + output, and to individual datasets within the file `statistics.h5` for HDF5 outout. For each seperate universe that has been run in + parallel as specified by the `UniversesInParallel` parameter, separate statistics will be written. For the ASCII format, the index of + the universe is given in the file names, so that the `*.0.out` files correspond to the first universe. For the HDF5 output, the + `statistics.h5` file contains a group that holds all data for each universe. + + \subsection output_smf Stellar Mass Functions + + For the ASCII format, the observed stellar mass functions, converted to the globally adopted IMF and Hubble parameter, are stored in + the `smfobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by + empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the redshift bin + edges and the publication reference. Each line in the data block contains the logarithmic stellar mass, the observed logarithmic number + density, the observational uncertainty, and the model prediction for this data point. The `smfmod.*.out` files contain the model stellar + mass function at each redshift for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar + mass bin followed by a model value for the stellar mass function at each redshift. + + For the HDF5 format, the stellar mass functions are stored in the `SMF` group. The `Sets` HDF5 compound gives an overview of the + different data sets. It has the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------- | ------ | :------: | ----------- | ------- + 0 | `Ndata` | int | 32 | 37 | The number of data points in this data set + 1 | `Offset` | int | 32 | 604 | The offset with respect to the first data set + 2 | `Redshift_min` | float | 32 | 0.2 | The minimum redshift + 3 | `Redshift_max` | float | 32 | 0.5 | The maximum redshift + 4 | `Tag` | string | 400 | Muzzin 2013 | A tag referencing the publication + + All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of + each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ------------------ | ----- | :------: | ------- | ------- + 0 | `Stellar_mass` | float | 32 | 8.39761 | The logarithmic stellar mass in solar masses + 1 | `Phi_observed` | float | 32 | -1.6404 | The logarithm of the observed number of galaxies per cubic Megaparsec and dex + 2 | `Sigma_observed` | float | 32 | 0.31167 | The observational uncertainty in dex + 3 | `Mean_ScaleFactor` | float | 32 | 0.75 | The mean scale factor \f$ a \f$ of the redshift bin + 4 | `Phi_model` | float | 32 | -1.6724 | The logarithm of the model prediction for the number density of galaxies + 5 | `Sigma_model` | float | 32 | 0.00661 | The model uncertainty in dex resulting from Poisson noise + + The model stellar mass function at each redshift is stored in the table `Model`. The first row contains the scale factors that correspond + to each column, while the first column contains the logarithmic stellar masses that correspond to each row. The remaining table entries + give the logarithm of the stellar mass function (in \f$ \mathrm{dex}^{-1} \mathrm{Mpc}^{-3} \f$) at the given stellar mass and scale factor. + + + \subsection output_fq Quenched Fractions + + For the ASCII format, the observed quenched fractions, converted to the globally adopted IMF and Hubble parameter, are stored in + the `fqobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by + empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the redshift bin + edges and the publication reference. Each line in the data block contains the logarithmic stellar mass, the observed quenched fraction, + the observational uncertainty, and the model prediction for this data point. The `fqmod.*.out` files contain the model quenched + fractions for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar mass + bin followed by a model value for the quenched fraction at each redshift. + + For the HDF5 format, the quenched fractions are stored in the `FQ` group. The `Sets` HDF5 compound gives an overview of the + different data sets. It has the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------- | ------ | :------: | ----------- | ------- + 0 | `Ndata` | int | 32 | 32 | The number of data points in this data set + 1 | `Offset` | int | 32 | 266 | The offset with respect to the first data set + 2 | `Redshift_min` | float | 32 | 0.2 | The minimum redshift + 3 | `Redshift_max` | float | 32 | 0.5 | The maximum redshift + 4 | `Tag` | string | 400 | Muzzin 2013 | A tag referencing the publication + + All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of + each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ------------------ | ----- | :------: | ------- | ------- + 0 | `Stellar_mass` | float | 32 | 10.5976 | The logarithmic stellar mass in solar masses + 1 | `Fq_observed` | float | 32 | 0.50100 | The observed fraction of quenched galaxies + 2 | `Sigma_observed` | float | 32 | 0.11413 | The observational uncertainty + 3 | `Mean_ScaleFactor` | float | 32 | 0.75 | The mean scale factor \f$ a \f$ of the redshift bin + 4 | `Fq_model` | float | 32 | 0.47875 | The model prediction for the fraction of quenched galaxies + 5 | `Sigma_model` | float | 32 | 0.01463 | The model uncertainty resulting from Poisson noise + + The model quenched fractions at each redshift are stored in the table `Model`. The first row contains the scale factors that correspond + to each column, while the first column contains the logarithmic stellar masses that correspond to each row. The remaining table entries + give the fraction of quenched galaxies at the given stellar mass and scale factor. + + + \subsection output_csfrd Cosmic Star Formation Rate Density + + For the ASCII format, the observed cosmic star formation rate densities, converted to the globally adopted IMF and Hubble parameter, are + stored in the `csfrdobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated + by empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points and the publication + reference. Each line in the data block contains the redshift, the observed logarithmic cosmic star formation rate density in solar masses per + year and cubic Megaparsec, the observational uncertainty, and the model prediction for this data point. The `csfrdmod.*.out` files contain the + redshift (first column) and the corresponding model cosmic star formation rate density (second column). + + For the HDF5 format, the cosmic star formation rate densities are stored in the `CSFRD` group. The `Sets` HDF5 compound gives an overview of the + data sets. It has the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------- | ------ | :------: | -------------- | ------- + 0 | `Ndata` | int | 32 | 4 | The number of data points in this data set + 1 | `Offset` | int | 32 | 106 | The offset with respect to the first data set + 2 | `Tag` | string | 400 | Duncan 2014 UV | A tag referencing the publication + + All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of + each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ---------------- | ----- | :------: | ------- | ------- + 0 | `Redshift` | float | 32 | 4.0 | The redshift + 1 | `Csfrd_observed` | float | 32 | -1.4438 | The observed cosmic star formation rate density in solar masses per year and cubic Megaparsec + 2 | `Sigma_observed` | float | 32 | 0.12806 | The observational uncertainty + 3 | `Csfrd_model` | float | 32 | -0.9831 | The model prediction for the cosmic star formation rate density + + The model predictions are stored in the table `Model`. The first column contains the redshift, and the second column contains the corresponding + cosmic star formation rate density. + + + \subsection output_ssfr Specific Star Formation Rates + + For the ASCII format, the observed specific star formation rates, converted to the globally adopted IMF and Hubble parameter, are stored in + the `ssfrobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by + empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points and the publication + reference. Each line in the data block contains the redshift, the logarithmic stellar mass, the observed specific star formation rate, + the observational uncertainty, and the model prediction for this data point. The `ssfrmod.*.out` files contain the model specific star + formation rates at each redshift for the stellar mass bins selected in the parameter file. Each line starts with the logarithmic stellar + mass bin followed by a model value for the specific star formation rate at each redshift. + + For the HDF5 format, the stellar mass functions are stored in the `SSFR` group. The `Sets` HDF5 compound gives an overview of the + different data sets. It has the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------- | ------ | :------: | ----------- | ------- + 0 | `Ndata` | int | 32 | 46 | The number of data points in this data set + 1 | `Offset` | int | 32 | 24 | The offset with respect to the first data set + 2 | `Tag` | string | 400 | Noeske 2007 | A tag referencing the publication + + All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of + each compound reflects the publication reference and the redshift bins. The observational data compounds have the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ---------------- | ----- | :------: | ------- | ------- + 0 | `Redshift` | float | 32 | 0.775 | The redshift + 1 | `Ssfr_observed` | float | 32 | -9.480 | The logarithm of the observed specific star formation rate in \f$ yr^{-1} \f$ + 2 | `Sigma_observed` | float | 32 | 0.335 | The observational uncertainty in dex + 3 | `Stellar_mass` | float | 32 | 10.397 | The logarithmic stellar mass in solar masses + 4 | `Ssfr_model` | float | 32 | -9.597 | The logarithm of the model prediction for the specific star formation rate + 5 | `Sigma_model` | float | 32 | 0.012 | The model uncertainty in dex resulting from Poisson noise + + The model specific star formation rates at each stellar mass and redshift are stored in the table `Model`. The first row contains the logarithmic + stellar masses that correspond to each column, while the first column contains the redshifts that correspond to each row. The remaining table entries + give the logarithm of the specific star formation rate (in \f$ yr^{-1} \f$) at the given stellar mass and scale factor. + + + \subsection output_wp Galaxy Clustering + + For the ASCII format, the observed projected correlation functions, converted to the globally adopted IMF and Hubble parameter, are stored in + the `ssfrobs.*.out` files (one for each universe). These files contain one data block for each observational data set, separated by + empty lines. Each data block is preceded by a header that starts with a `#`, followed by the number of data points, the stellar mass bin edges, + maximum line-of-sight projection distance \f$ \pi_\mathrm{max} \f$, and the publication reference. Each line in the data block contains the + projected distance, the observed projected correlation function, the observational uncertainty, the model prediction for this data point, and + the model error, based on Poisson noise. The `ximod.*.out` files contain the model predictions for the 3D correlation functions in the same + stellar mass bins as the observations. The first line starts with a `#` followed by all stellar mass bins. The subsequent lines contain the + 3D distance, the model 3D correlation function, and the model uncertainty for each stellar mass bin. + + For the HDF5 format, the clustering data are stored in the `Clustering` group. The `Sets` HDF5 compound gives an overview of the + different data sets. It has the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------- | ------ | :------: | ------- | ------- + 0 | `Ndata` | int | 32 | 27 | The number of data points in this data set + 1 | `Offset` | int | 32 | 50 | The offset with respect to the first data set + 2 | `Minimum_Mass` | float | 32 | 10.3374 | The minimum logarithmic stellar mass + 3 | `Maximum_Mass` | float | 32 | 10.8374 | The maximum logarithmic stellar mass + 4 | `Pi_max` | float | 32 | 14.7471 | The maximum projected distance in Megaparsecs + 5 | `Tag` | string | 400 | Li 2006 | A tag referencing the publication + + All of the observed data sets are located in the `data` subgroup, and are organised in a HDF5 compound for each data set. The name of + each compound reflects the publication reference and the stellar mass bins. The observational data compounds have the following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ---------------- | ----- | :------: | ------- | ------- + 0 | `Radius` | float | 32 | 0.3391 | The projected radius in Megaparsecs + 1 | `Wp_observed` | float | 32 | 322.66 | The projected galaxy auto-correltion function in Megaparsecs + 2 | `Sigma_observed` | float | 32 | 135.46 | The observational uncertainty in Megaparsecs + 3 | `Wp_model` | float | 32 | 303.63 | The model prediction for the projected galaxy auto-correltion function + 4 | `Sigma_model` | float | 32 | 16.79 | The model uncertainty in Megaparsecs resulting from Poisson noise + + The model 3D auto-correlation functions for each stellar mass bin are stored in the `Model_3D` subgroup, and are organised in a HDF5 compound + for each data set. The name of each compound reflects the publication reference and the stellar mass bins. The data compounds have the + following structure: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ---------- | ----- | :------: | ------- | ------- + 0 | `Radius` | float | 32 | 1.0015 | The 3D radius in Megaparsecs + 1 | `Xi` | float | 32 | 41.049 | The 3D galaxy auto-correltion function + 2 | `Sigma_xi` | float | 32 | 1.9703 | The model uncertainty + + + \subsection output_chi2 Model likelihoods + + To check how well the model fits the observed data, Emerge writes out the calculated \f$ \chi^2 \f$ values. For the ASCII output, the values + are stored in the `chi2.out` file. The header line starts with a `#` and then lists all global statistics that have been used in the fit. The + following lines then list the \f$ \chi^2 \f$ values for each universe. Each line starts with the universe index, and then gives the + total \f$ \chi^2 \f$ value, followed by the \f$ \chi^2 \f$ values for the individual statistics. For the HDF5 output, the \f$ \chi^2 \f$ values + are stored in the `Chi2` table in the `statistics.h5` file for each universe. + + + \subsection output_param Adopted model parameters + + The model parameters that have been used to run each universe are stored for the HDF5 output format in the `statistics.h5` file. The HDF5 + compound `Model_Parameters` within each universe group contains all parameters as double (64 bit) values. + + + \section output_galaxy_cat Galaxy Catalogues + + If Emerge is not called with a flag behind the parameter file, which corresponds to the code mode 0, mock galaxy catalogue are written out + as long as the option `WRITE_GALAXY_CATALOG` has been set. The output format (ASCII or HDF5) can be chosen with the parameter `OutputFormat` + in the parameter file. For each output redshift that has been specified with the parameter `OutputRedshifts`, a file containing all galaxies + in the simulation box that are more massive than the threshold `OutputMassThreshold` will be written written. The name of each file begins + with `galaxies.S`, followed by the snapshot number that corresponds to its redshift, and a file extension, e.g. `galaxies.S90.out` for + snapshot 90 and ASCII format. Note that the mock catalogue is only written for the first universe if `UniversesInParallel` is larger than 1. + + All ASCII files have a header starting with a `#` which lists the contents of each column, as well as several key parameters that have been + chosen to create the catalogue. The table following this header then contains the galaxy and halo properties of one system in each line. The + HDF5 files contain a compound dataset that lists the properties of each system. The key parameters are stored as attributes of this dataset: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | ---------------------- | ------ | :------: | ------- | ------- + 0 | `Scale Factor` | float | 32 | 1.0 | Scale factor of the catalogue + 1 | `Box Size` | double | 64 | 100.0 | Box side length in \f$ \mathrm{Mpc} \f$ + 2 | `Hubble Parameter` | double | 64 | 0.6781 | Hubble parameter \f$ h = H / (100 \mathrm{kms}^{-1}\mathrm{Mpc}^{-1}) \f$ + 3 | `Omega_0` | double | 64 | 0.308 | Matter density parameter. + 4 | `Omega_Lambda` | double | 64 | 0.692 | Dark energy density parameter + 5 | `Omega_Baryon_0` | double | 64 | 0.0484 | Baryon density parameter + 6 | `Minimum Stellar Mass` | double | 64 | 7.0 | Logarithm of the minimum stellar mass in \f$ \mathrm{M}_\odot \f$ + + Each system in the dataset has several properties: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------------- | -------------- | :------: | ------- | ------- + 0 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log \f$ \mathrm{M}_\odot \f$ + 1 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 2 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log \f$ \mathrm{M}_\odot \f$ + 3 | `Halo_mass_host` | float | 32 | 12.41 | Virial mass of the host halo in log \f$ \mathrm{M}_\odot \f$ + 4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached + 5 | `Scale_half_mass` | float | 32 | 0.69 | Scale factor when half of the current virial mass was reached + 6 | `Halo_radius` | float | 32 | 373.81 | Virial radius in \f$ \mathrm{kpc} \f$ + 7 | `Concentration` | float | 32 | 4.41 | Concentration parameter (\f$ c = R_\mathrm{vir}/R_\mathrm{s} \f$) + 8 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \f$ \lambda \f$ + 9 | `Stellar_mass` | float | 32 | 10.52 | Stellar mass of the galaxy in log \f$ \mathrm{M}_\odot \f$ + 10 | `SFR` | float | 32 | 0.72 | Star formation rate of the galaxy in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 11 | `Intra_cluster_mass` | float | 32 | 9.051 | Stellar mass of the halo (excluding satellites) in log \f$ \mathrm{M}_\odot \f$ + 12 | `Stellar_mass_obs` | float | 32 | 10.71 | Observed stellar mass of the galaxy in log \f$ \mathrm{M}_\odot \f$ + 13 | `SFR_obs` | float | 32 | 0.59 | Observed star formation rate of the galaxy in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 14 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 15 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 16 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 17 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 18 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 19 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 20 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) + 21 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo + 22 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) + 23 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + + If the parameter `NumOutputFiles` is set to a number larger than 1, each mock catalogue at a fixed redshift will be split into several files. The + file index will be appended to the filename before the file extension, e.g. `galaxies.S90.0.h5`, `galaxies.S90.1.h5`, `...`. + + + \section output_halo_cat Halo Catalogues + + Similar to the galaxy catalogue output, Emerge can create halo catalogues if called without a flag (code mode 0), and the option `WRITE_HALO_CATALOG` + has been set. These halo catalogues include the subhaloes that correspond to orphan galaxies, which can be useful if the catalogues are used to + link galaxies to haloes, e.g. for subhalo abundance matching. The output format (ASCII or HDF5) is again chosen with the parameter `OutputFormat` + and the output redshifts are specified with `OutputRedshifts`. The files start with `haloes.S`, followed by the snapshot number and a file extension, + e.g. `haloes.S90.h5`. The mock catalogue is again only written for the first universe. The ASCII files have a similar header as the galaxy catalogue + files, and are followed by a table that lists the properties of one halo in each line. For the HDF5 files, the halo properties are stored in a + compound dataset, with key parameters stored as attributes. The following halo properties are written: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------------- | -------------- | :------: | ------- | ------- + 0 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log \f$ \mathrm{M}_\odot \f$ + 1 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 2 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log \f$ \mathrm{M}_\odot \f$ + 3 | `Halo_mass_host` | float | 32 | 12.41 | Virial mass of the host halo in log \f$ \mathrm{M}_\odot \f$ + 4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached + 5 | `Scale_half_mass` | float | 32 | 0.69 | Scale factor when half of the current virial mass was reached + 6 | `Halo_radius` | float | 32 | 373.81 | Virial radius in \f$ \mathrm{kpc} \f$ + 7 | `Concentration` | float | 32 | 4.41 | Concentration parameter (\f$ c = R_\mathrm{vir}/R_\mathrm{s} \f$) + 8 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \f$ \lambda \f$ + 9 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 10 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 11 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 12 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 13 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 14 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 15 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) + 16 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo + 17 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) + 18 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + + To generate halo catalogues with haloes that correspond to orphan galaxies, it is recommended to set the normalisation of the conversion + efficiency (\f$ \epsilon_0 \f$ and \f$ \epsilon_z \f$), and the stripping parameter \f$ f_\mathrm{s} \f$ to zero. In this case, the stellar + mass in each halo will be zero, and the dynamical friction times will be based only on the halo mass. + + + \section output_mainbranch Main Branches + + To track galaxies and haloes through cosmic time, Emerge can write the properties of systems on the main branch of the merger tree. For this, + Emerge must be called without a flag (code mode 0), and the option `WRITE_MAINBRANCH` has to be set. The output format (ASCII or HDF5) is chosen + with the parameter `OutputFormat`. Galaxies are selected at a specific redshift with `MainBranchRedshift`, and in several mass bins with + `MainBranchMasses` and `MainBranchBinSize`, while `MainBranchMassType` specifies which mass is used for this selection (0: halo mass, 1: stellar + mass). For each galaxy that fulfils this criterion, all main progenitors and descendants are written as a data block. The file name starts with + `mainbranches.S`, followed by the snapshot number that corresponds the selection redshift, and a file extension, e.g. `mainbranches.S90.out` for + the main branches that have been selected to fulfil the mass criterion at snapshot 90. + + The ASCII files have a header block starting with a `#`, which lists the contents of each column, as well as several key parameters, such as the + selection mass bins and the box size. After the header block, each main branch is written as a data block, separated by empty lines. Each data + block starts with an index given by the mass bin and the main branch number, e.g. `#000_0000000` corresponds to the first mass bin and the first + main branch in that bin. After this index, a table lists the properties of each galaxy on the main branch of the merger tree. + + The HDF5 files include an attribute `Selection_Redshift` listing the redshift for which galaxies have been selected. The main branches are stored + within HDF5 groups for each mass bin, e.g. `MainBranch_M000` for the first mass bin. Each of these groups has the following attributes: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | --------------- | ------ | :------: | -------------- | ------- + 0 | `Mass_bin` | double | 64 | 10.0 | Selection mass in log \f$ \mathrm{M}_\odot \f$ + 1 | `Mass_bin_size` | double | 64 | 0.1 | Selection mass bin size in dex + 2 | `Mass_Type` | string | 72 or 96 | `Stellar_Mass` | Which mass was used in the selection (Halo_Mass or Stellar_Mass) + + The main branches are written as HDF5 compound datasets in each mass bin group. The name of each dataset stars with `Tree_` followed by the Index + of the main branch. Each system in the main branch has the following properties: + +  #  | HDF5 name | Type | Bit size | Example | Comment + :-----------: | -------------------- | -------------- | :------: | ------- | ------- + 0 | `Scale_factor | float | 32 | 1.00 | Scale factor \f$ a \f$ of the system + 1 | `Halo_mass` | float | 32 | 12.41 | Virial mass of the halo in log \f$ \mathrm{M}_\odot \f$ + 2 | `Halo_growth_rate` | float | 32 | 471.76 | Growth rate of the halo in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 3 | `Halo_mass_peak` | float | 32 | 12.41 | Peak virial mass over history in log \f$ \mathrm{M}_\odot \f$ + 4 | `Scale_peak_mass` | float | 32 | 1.00 | Scale factor when peak virial mass was reached + 5 | `Halo_radius` | float | 32 | 373.81 | Virial radius in \f$ \mathrm{kpc} \f$ + 6 | `Concentration` | float | 32 | 4.41 | Concentration parameter (\f$ c = R_\mathrm{vir}/R_\mathrm{s} \f$) + 7 | `Halo_spin` | float | 32 | 0.038 | Halo spin parameter \f$ \lambda \f$ + 8 | `Stellar_mass` | float | 32 | 10.52 | Stellar mass of the galaxy in log \f$ \mathrm{M}_\odot \f$ + 9 | `SFR` | float | 32 | 0.72 | Star formation rate of the galaxy in \f$ \mathrm{M}_\odot \mathrm{yr}^{-1} \f$ + 10 | `Intra_cluster_mass` | float | 32 | 9.051 | Stellar mass of the halo (excluding satellites) in log \f$ \mathrm{M}_\odot \f$ + 11 | `X_pos` | float | 32 | 8.68 | X-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 12 | `Y_pos` | float | 32 | 86.19 | Y-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 13 | `Z_pos` | float | 32 | 7.81 | Z-Position of the system in the simulation box in \f$ \mathrm{Mpc} \f$ + 14 | `X_vel` | float | 32 | -213.27 | X-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 15 | `Y_vel` | float | 32 | 132.13 | Y-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 16 | `Z_vel` | float | 32 | -343.57 | Z-Velocity of the system in \f$ \mathrm{kms}^{-1} \f$ + 17 | `Type` | unsigned short | 16 | 0 | System type (0: central, 1: satellite, 2: orphan) + 18 | `Halo_ID` | long long int | 64 | 12345 | ID of the halo + 19 | `Desc_ID` | long long int | 64 | -1 | ID of the descendant (-1 if none) + 20 | `Up_ID` | long long int | 64 | -1 | ID of the host halo (-1 if halo is a host halo itself) + + A single dataset always traces a single system from the root at \f$ z=0 \f$ up to its leaf at high redshift, i.e. each scale factor is listed only + once. The output allways follows the main progenitor in halo mass, i.e. the most massive progenitor halo is used. + + + \page disclaimer Disclaimer + + The performance and accuracy of Emerge strongly depend on a number of variables, such as the \f$ N-\mathrm{body} \f$ simulations and the derived + halo merger trees, the observational data used to fit the model, and the code parameters. Emerge comes without any warranty, and without any + guarantee that it produces correct results, although the resulting galaxies will undoubtedly be the most elaborate on the market in any case. + The numerical parameter values used in the provided parameter file and in this manual do not represent a specific recommendation by the author, + but are merely a combination that has been found to work for a particular setup. Exploring parameter space is a difficult task, and it is never + guaranteed that an algorithm will converge towards the global maximum of the likelihood function. The user is therefore encouraged to test + different parameters to achieve sufficient accuracy for any given setup. If in doubt about some aspect of Emerge, a sensible strategy is to read + the source code and the scientific paper to understand the details. + + + */ diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..b310ce1 --- /dev/null +++ b/src/output.c @@ -0,0 +1,1600 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File output.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file galaxies.c +/// \brief Contains functions that store computed data in files +/// +/// This file contains all functions that print computed galaxy and halo data to output files, +/// except for that function that prints the statistics (see statistics.c). +/// A function that prints all galaxies at a specific scale factor to output files is also included. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + +#ifdef HDF5_SUPPORT +#include +#endif + + +#ifdef WRITE_GALAXY_CATALOG +/*! \brief This function collects all galaxy data and prints it + * + * This function collects the galaxy data from all tasks and writes it to All.NumOutputFiles files. There + * are All.NumFilesInParallel files written in parallel. Each master task collects the galaxy/halo data from + * its slave tasks and writes it. If there is one output file it will be called galaxies.out otherwise there + * will be n files called galaxies.i where i < n. + */ +void output_galaxies(void) +{ + int i, j, k, ihalo, iprog, filenr, masterTask, lastTask, ngroups, maxlen, task; + int left_to_send, p, send_this_turn, offset, nhaloout; + float sigmaobs, mstarobs, sfrobs, scatter, amhalf; + char buf[NSTRING]; + FILE *fd; + MPI_Status status; + + //Structure that stores all halo/galaxy properties that will be saved + struct haloout { + float mh,mdot,mhp,mhh,amp,amh,r,c,l,ms,sfr,icm,mso,sfro,x,y,z,u,v,w; + unsigned short t; + long long hid, did, uid; + } *hp, hsend; + +#ifdef HDF5_SUPPORT + hid_t gal_file, hdf5_dataspace_memory, hdf5_dataspace_in_file, hdf5_dtype, hdf5_ftype, hdf5_filespace; + hid_t hdf5_properties, hdf5_dataset; + hid_t hdf5_att_space, hdf5_att_redshift, hdf5_att_lbox, hdf5_att_hubble; + hid_t hdf5_att_omega0, hdf5_att_omegal, hdf5_att_omegab, hdf5_att_mmin; + herr_t hdf5_status; + hsize_t dims[1], maxdims[1], count[1], start[1]; + int sendsum = 0; + double tmp; + char path[NSTRING]; +#endif + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Writing galaxies in 1st universe to %d files...\n", All.fullline,All.startline,All.NumOutputFiles * All.Noutputredshifts); + + //Check if there are enough tasks to write all output files + if (All.NTaskPerUniverse < All.NumOutputFiles && ThisTask == 0) + endrun("Fatal error. Number of processors must be larger or equal than All.NumOutputFiles."); + + //Allocate CommBuffer + CommBuffer = emalloc("CommBuffer", All.BufferSize * 1024 * 1024); + + //Get maximum number of haloes that fit in the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / sizeof(struct halo); + + //Assign processors to output files + distribute_file(All.NTaskPerUniverse, All.NumOutputFiles, 0, 0, All.NTaskPerUniverse - 1, &filenr, &masterTask, &lastTask); + + //Go through all output redshifts + for(i = 0; i < All.Noutputredshifts; i++) + { + //If the output format has been set to HDF5 + if (All.OutputFormat == 2) + { + //Check if HDF5 libraries are set +#ifdef HDF5_SUPPORT + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/galaxies.S%d.%d.h5", All.OutputDir, Output_iscale[i], filenr); + else + sprintf(buf, "%s/galaxies.S%d.h5", All.OutputDir, Output_iscale[i]); + //If not return +#else + if (ThisTask == 0) printf("%s Output format has been set to 2 but HDF5 support was not enabled.\n",All.startline); + return; +#endif + } + //Otherwise use standard ascii files + else + { + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/galaxies.S%d.%d", All.OutputDir, Output_iscale[i], filenr); + else + sprintf(buf, "%s/galaxies.S%d.out", All.OutputDir, Output_iscale[i]); + } + + //Get number of groups + ngroups = All.NumOutputFiles / All.NumFilesInParallel; + if((All.NumOutputFiles % All.NumFilesInParallel)) ngroups++; + + //For each group do... + for(j = 0; j < ngroups; j++) + { + //This task will be processed now + if((filenr / All.NumFilesInParallel) == j && MasterTask == 0) + { //The masterTask opens the file + if(ThisTask == masterTask) + { //If the hdf5 output format has been selected + if (All.OutputFormat == 2) + { //Check if the libraries have been included +#ifdef HDF5_SUPPORT + //Create hdf5 file + gal_file = H5Fcreate(buf, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + //Print to screen + printf("%s Writing output file at z = %f: '%s' (file %d of %d)\n",All.startline, OutputRedshifts[i], buf, filenr+1, All.NumOutputFiles); + //Specify data type + hdf5_dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct haloout)); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass", HOFFSET(struct haloout, mh), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_growth_rate", HOFFSET(struct haloout, mdot), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass_peak", HOFFSET(struct haloout, mhp), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass_host", HOFFSET(struct haloout, mhh), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_peak_mass", HOFFSET(struct haloout, amp), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_half_mass", HOFFSET(struct haloout, amh), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_radius", HOFFSET(struct haloout, r), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Concentration", HOFFSET(struct haloout, c), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_spin", HOFFSET(struct haloout, l), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Stellar_mass", HOFFSET(struct haloout, ms), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "SFR", HOFFSET(struct haloout, sfr), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Intra_cluster_mass", HOFFSET(struct haloout, icm), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Stellar_mass_obs", HOFFSET(struct haloout, mso), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "SFR_obs", HOFFSET(struct haloout, sfro), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_pos", HOFFSET(struct haloout, x), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_pos", HOFFSET(struct haloout, y), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_pos", HOFFSET(struct haloout, z), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_vel", HOFFSET(struct haloout, u), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_vel", HOFFSET(struct haloout, v), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_vel", HOFFSET(struct haloout, w), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Type", HOFFSET(struct haloout, t), H5T_NATIVE_USHORT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_ID", HOFFSET(struct haloout, hid), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Desc_ID", HOFFSET(struct haloout, did), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Up_ID", HOFFSET(struct haloout, uid), H5T_NATIVE_LLONG); + //Specify file type + k = 0; + hdf5_ftype = H5Tcreate(H5T_COMPOUND, 4 * 20 + 2 + 8 * 3); + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_growth_rate", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass_peak", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass_host", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_peak_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_half_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_radius", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Concentration", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_spin", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Stellar_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "SFR", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Intra_cluster_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Stellar_mass_obs", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "SFR_obs", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Type", k, H5T_STD_U16LE); k+=2; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Desc_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Up_ID", k, H5T_STD_I64LE); k+=8; +#endif + } + //Otherwise open standard ascii file + else + { //Open file + if(!(fd = fopen(buf, "w"))) + { //If not possible print to screen and abort + printf("%s Can't open file `%s' for writing output.\n",All.startline,buf); + endrun("file open error"); + } + //If file can be opened print to screen + printf("%s Writing output file at z = %f: '%s' (file %d of %d)\n",All.startline, OutputRedshifts[i], buf, filenr+1, All.NumOutputFiles); + //Write ascii header + task = 0; + fprintf(fd,"#Halo_mass(%d)",task); task++; + fprintf(fd," Halo_growth_rate(%d)",task); task++; + fprintf(fd," Halo_mass_peak(%d)",task); task++; + fprintf(fd," Halo_mass_host(%d)",task); task++; + fprintf(fd," Scale_peak_mass(%d)",task); task++; + fprintf(fd," Scale_half_mass(%d)",task); task++; + fprintf(fd," Halo_radius(%d)",task); task++; + fprintf(fd," Concentration(%d)",task); task++; + fprintf(fd," Halo_spin(%d)",task); task++; + fprintf(fd," Stellar_mass(%d)",task); task++; + fprintf(fd," SFR(%d)",task); task++; + fprintf(fd," Intra_cluster_mass(%d)",task); task++; + fprintf(fd," Stellar_mass_obs(%d)",task); task++; + fprintf(fd," SFR_obs(%d)",task); task++; + fprintf(fd," X_pos(%d)",task); task++; + fprintf(fd," Y_pos(%d)",task); task++; + fprintf(fd," Z_pos(%d)",task); task++; + fprintf(fd," X_vel(%d)",task); task++; + fprintf(fd," Y_vel(%d)",task); task++; + fprintf(fd," Z_vel(%d)",task); task++; + fprintf(fd," Type(%d)",task); task++; + fprintf(fd," Halo_ID(%d)",task); task++; + fprintf(fd," Desc_ID(%d)",task); task++; + fprintf(fd," Up_ID(%d)",task); task++; + fprintf(fd,"\n"); + fprintf(fd,"#Emerge Branch: %s\n",BRANCH); + fprintf(fd,"#Emerge Version: %s\n",VERSION); + fprintf(fd,"#Model Name: %s\n",All.model_name); + fprintf(fd,"#Scale Factor: %f\n",ScaleFactor[Output_iscale[i]]); + fprintf(fd,"#Box Size: %f (Mpc)\n",All.Lbox*All.x_unit); + fprintf(fd,"#Hubble Parameter: %f\n",All.h_100); + fprintf(fd,"#Omega_0: %f\n",All.Omega_0); + fprintf(fd,"#Omega_Lambda: %f\n",All.Omega_Lambda_0); + fprintf(fd,"#Omega_Baryon: %f\n",All.Omega_Baryon_0); + fprintf(fd,"#Minimum Stellar Mass: %f (log Msun)\n",log10(All.minmass*All.m_unit)); + fprintf(fd,"#Halo_mass: Current virial mass of the halo (log Msun).\n"); + fprintf(fd,"#Halo_growth_rate: Growth rate of the halo (Msun/yr)\n"); + fprintf(fd,"#Halo_mass_peak: Peak virial mass of the halo through its history (log Msun).\n"); + fprintf(fd,"#Halo_mass_host: Current virial mass of the host halo (log Msun).\n"); + fprintf(fd,"#Scale_peak_mass: Scale Factor when halo had its peak mass.\n"); + fprintf(fd,"#Scale_half_mass: Scale Factor when halo first had half of its peak mass.\n"); + fprintf(fd,"#Halo_radius: Current virial radius of the halo (kpc).\n"); + fprintf(fd,"#Concentration: Virial radius divided by halo scale length.\n"); + fprintf(fd,"#Halo_spin: Halo spin parameter.\n"); + fprintf(fd,"#Stellar_mass: Intrinsic stellar mass of the galaxy (log Msun).\n"); + fprintf(fd,"#SFR: Star formation rate of the galaxy (Msun/yr).\n"); + fprintf(fd,"#Intra_cluster_mass: Stellar mass in the halo (log Msun).\n"); + fprintf(fd,"#Stellar_mass_obs: Observed stellar mass including observation error (log Msun).\n"); + fprintf(fd,"#SFR_obs: Observed star formation rate including observation error (Msun/yr).\n"); + fprintf(fd,"#X_pos: Comoving galaxy position along the X axis (Mpc).\n"); + fprintf(fd,"#Y_pos: Comoving galaxy position along the Y axis (Mpc).\n"); + fprintf(fd,"#Z_pos: Comoving galaxy position along the Z axis (Mpc).\n"); + fprintf(fd,"#X_vel: Peculiar/physical velocity of the galaxy along the X axis (km/s).\n"); + fprintf(fd,"#Y_vel: Peculiar/physical velocity of the galaxy along the Y axis (km/s).\n"); + fprintf(fd,"#Z_vel: Peculiar/physical velocity of the galaxy along the Z axis (km/s).\n"); + fprintf(fd,"#Type: Whether the galaxy is a central (0), satellite (1), or orphan (2).\n"); + fprintf(fd,"#Halo_ID: Halo ID that corresponds to the ID in the original halo catalogue.\n"); + fprintf(fd,"#Desc_ID: ID of the descendant halo. If there is none then Desc_ID = -1.\n"); + fprintf(fd,"#Up_ID: ID of the most massive halo. If this is a main halo then Up_ID = -1.\n"); + } + } + + //Determine number of systems that will be printed on each task (depending on redshift and mass) + nhaloout = 0; + for (ihalo = 0; ihalo < Nhalos; ihalo++) + if (H[ihalo].iscale == Output_iscale[i] && H[ihalo].gone == 0 && H[ihalo].mstar >= All.minmass) nhaloout++; + + if (nhaloout > 0) + { + +#ifdef HDF5_SUPPORT + if (ThisTask == masterTask && All.OutputFormat == 2) + { + //Set the path name for the galaxy data set + sprintf(path,"/Galaxies"); + //Initial size is the number of haloes on the master task + dims[0] = nhaloout; + //Allow for variable total size + maxdims[0] = H5S_UNLIMITED; + //Create the data space + hdf5_dataspace_in_file = H5Screate_simple(1, dims, maxdims); + //Create the data properties + hdf5_properties = H5Pcreate(H5P_DATASET_CREATE); + hdf5_status = H5Pset_chunk(hdf5_properties, 1, dims); // set chunk size + hdf5_status = H5Pset_shuffle(hdf5_properties); // reshuffle bytes to get better compression ratio + hdf5_status = H5Pset_deflate(hdf5_properties, 9); // gzip compression level 9 + hdf5_status = H5Pset_fletcher32(hdf5_properties); // Fletcher32 checksum on dataset + //If filters could be set use them to create the data set + if(H5Pall_filters_avail(hdf5_properties)) + hdf5_dataset = H5Dcreate(gal_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, hdf5_properties, H5P_DEFAULT); + //Otherwise create the default data set + else + hdf5_dataset = H5Dcreate(gal_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Initialise the number of haloes in the set to zero + sendsum = 0; + //Define Scalar for Attributes + hdf5_att_space = H5Screate(H5S_SCALAR); + //Write Redshift as attribute to dataset + hdf5_att_redshift = H5Acreate(hdf5_dataset, "Scale Factor", H5T_NATIVE_FLOAT, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_redshift, H5T_IEEE_F32LE, &ScaleFactor[Output_iscale[i]]); + H5Aclose(hdf5_att_redshift); + //Write Box Size as attribute to dataset + tmp = All.Lbox*All.x_unit; + hdf5_att_lbox = H5Acreate(hdf5_dataset, "Box Size", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_lbox, H5T_IEEE_F64LE, &tmp); + H5Aclose(hdf5_att_lbox); + //Write Hubble parameter as attribute to dataset + hdf5_att_hubble = H5Acreate(hdf5_dataset, "Hubble Parameter", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_hubble, H5T_IEEE_F64LE, &All.h_100); + H5Aclose(hdf5_att_hubble); + //Write Omega_0 as attribute to dataset + hdf5_att_omega0 = H5Acreate(hdf5_dataset, "Omega_0", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omega0, H5T_IEEE_F64LE, &All.Omega_0); + H5Aclose(hdf5_att_omega0); + //Write Omega_Lambda as attribute to dataset + hdf5_att_omegal = H5Acreate(hdf5_dataset, "Omega_Lambda", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omegal, H5T_IEEE_F64LE, &All.Omega_Lambda_0); + H5Aclose(hdf5_att_omegal); + //Write Omega_Baryon as attribute to dataset + hdf5_att_omegab = H5Acreate(hdf5_dataset, "Omega_Baryon", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omegab, H5T_IEEE_F64LE, &All.Omega_Baryon_0); + H5Aclose(hdf5_att_omegab); + //Write minimum stellar mass as attribute to dataset + tmp = log10(All.minmass*All.m_unit); + hdf5_att_mmin = H5Acreate(hdf5_dataset, "Minimum Stellar Mass", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_mmin, H5T_IEEE_F64LE, &tmp); + H5Aclose(hdf5_att_mmin); + } +#endif + + //Now go through all tasks that are member of this group + for(task = masterTask, offset = 0; task <= lastTask; task++) + { + //if this task is processed + if(task == ThisTask) + { + //We need to get those haloes + left_to_send = nhaloout; + + //Tell this to the other tasks + for(p = masterTask; p <= lastTask; p++) + if(p != ThisTask) + MPI_Send(&left_to_send, 1, MPI_INT, p, TAG_NHALOS, MPI_COMM_WORLD); + } + //Reveive the number of haloes from active task + else MPI_Recv(&left_to_send, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD, &status); + + //Now send the haloes in increments + while(left_to_send > 0) + { + //Compute how many haloes we can send this turn + send_this_turn = left_to_send; + //If they do not all fit into the CommBuffer set to the maximum + if(send_this_turn > maxlen) send_this_turn = maxlen; + + //Sending task + if(ThisTask == task) + { + //Use the haloout struct for the CommBuffer + hp = (struct haloout *) CommBuffer; + //Initialise the number of systems sent to zero + ihalo = 0; + //Go through all systems until the maximum that can be sent + while (ihalo < send_this_turn) + { //If we are at the right scale factor and the halo is not gone send it to the master + if (H[offset].iscale == Output_iscale[i] && H[offset].gone == 0 && H[offset].mstar >= All.minmass) + { + //Set the scatter in stellar mass + scatter = get_gaussian_random_number(offset); + //Compute the observational error and the observed stellar mass + sigmaobs = All.obssigma0+(1./H[offset].a-1.)*All.obssigmaz; + if (1./H[offset].a-1. > ZMAX_SMFERR) sigmaobs = All.obssigma0+(ZMAX_SMFERR)*All.obssigmaz; + mstarobs = H[offset].mstar * pow(10.,sigmaobs*scatter); + //Set the scatter in the star formation rate + scatter = get_gaussian_random_number(offset + RANDOM_NUMBER_TABLE/2); + //Compute the observed star formation rate + sfrobs = H[offset].sfr * pow(10.,sigmaobs*scatter); + + //Find scale factor when virial mass was half of maximum value + amhalf = ScaleFactor[H[H[offset].impeak].iscale]; + iprog = H[H[offset].impeak].iprog; + if (iprog >=0 ) + { + while (H[iprog].mvir > 0.5 * H[H[offset].impeak].mvir) + { + if (H[iprog].iprog < 0) break; + iprog = H[iprog].iprog; + } + amhalf = ScaleFactor[H[iprog].iscale]; + } + + //Set all halo/galaxy properties + hsend.mh = log10(H[offset].mvir*All.m_unit); + hsend.mdot = H[offset].mdotbary*All.m_unit/All.t_unit/All.f_baryon; + hsend.mhp = log10(H[H[offset].impeak].mvir*All.m_unit); +#ifdef COMPUTE_ICM + hsend.mhh = log10(H[H[offset].ihost].mvir*All.m_unit); +#else + hsend.mhh = 0.0; +#endif + hsend.amp = ScaleFactor[H[H[offset].impeak].iscale]; + hsend.amh = amhalf; + hsend.r = H[offset].rvir*All.x_unit*1.e3; + hsend.c = H[offset].c; + hsend.l = H[offset].lambda; + hsend.ms = log10(H[offset].mstar*All.m_unit); + hsend.sfr = H[offset].sfr*All.m_unit/All.t_unit; + hsend.icm = log10(H[offset].icm*All.m_unit); + hsend.mso = log10(mstarobs*All.m_unit); + hsend.sfro = sfrobs*All.m_unit/All.t_unit; + hsend.x = H[offset].pos[0]; + hsend.y = H[offset].pos[1]; + hsend.z = H[offset].pos[2]; + hsend.u = H[offset].vel[0]; + hsend.v = H[offset].vel[1]; + hsend.w = H[offset].vel[2]; + hsend.t = H[offset].type; + hsend.hid = (long long)(H[offset].haloid)-1; + hsend.did = (long long)(H[offset].descid)-1; + hsend.uid = (long long)(H[offset].upid)-1; + + //Write this system to the CommBuffer + *hp++ = hsend; + //Increment the number of systems that are being sent + ihalo++; + } + //Increment the halo index + offset++; + } + } + + //Receive haloes + if(ThisTask == masterTask && task != masterTask) MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD, &status); + + //Send haloes + if(ThisTask != masterTask && task == ThisTask) MPI_Ssend(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, masterTask, TAG_HDATA, MPI_COMM_WORLD); + + //Collect haloes and write to file + if(ThisTask == masterTask) + { + + //If the output format is hdf5 + if (All.OutputFormat == 2) + { +#ifdef HDF5_SUPPORT + //Set the starting index and the count + start[0] = sendsum; + count[0] = send_this_turn; + //Add the number of haloes that are sent this turn to the total number and set the size to the total + sendsum += send_this_turn; + dims[0] = sendsum; + //If the total size is larger than the initial set size for the data set extend it + if (sendsum > nhaloout) hdf5_status = H5Dset_extent(hdf5_dataset, dims); + //Get the data space of the data set + hdf5_filespace = H5Dget_space(hdf5_dataset); + //Select the hyperslab that corresponds to the current offset + hdf5_status = H5Sselect_hyperslab(hdf5_filespace, H5S_SELECT_SET, start, NULL, count, NULL); + //Set the size to the number of systems sent this turn + dims[0] = send_this_turn; + //Create a data space for the memory + hdf5_dataspace_memory = H5Screate_simple(1, dims, NULL); + //Write the data set + hdf5_status = H5Dwrite(hdf5_dataset, hdf5_dtype, hdf5_dataspace_memory, hdf5_filespace, H5P_DEFAULT, CommBuffer); + //Close the data space of the data set + H5Sclose(hdf5_filespace); + //Close the data space for the memory + H5Sclose(hdf5_dataspace_memory); +#endif + }//End HDF5 output + //Otherwise simply write the properties to an ascii file + else + { //Use the haloout struct for the CommBuffer + hp = (struct haloout *) CommBuffer; + //Go through all systems that were sent this turn + for (ihalo = 0; ihalo < send_this_turn; ihalo++) + { //Write a new line in the ascii file + fprintf(fd,"%f %e %f %f %f %f %f %f %f %f %e %f %f %e %f %f %f %f %f %f %hu %lld %lld %lld\n", + hp[ihalo].mh, + hp[ihalo].mdot, + hp[ihalo].mhp, + hp[ihalo].mhh, + hp[ihalo].amp, + hp[ihalo].amh, + hp[ihalo].r, + hp[ihalo].c, + hp[ihalo].l, + hp[ihalo].ms, + hp[ihalo].sfr, + hp[ihalo].icm, + hp[ihalo].mso, + hp[ihalo].sfro, + hp[ihalo].x, + hp[ihalo].y, + hp[ihalo].z, + hp[ihalo].u, + hp[ihalo].v, + hp[ihalo].w, + hp[ihalo].t, + hp[ihalo].hid, + hp[ihalo].did, + hp[ihalo].uid); + }//End loop through all haloes + }//End ascii output + }//End master task (writer task) + //Decrease the number of haloes left to send by the number sent this turn + left_to_send -= send_this_turn; + }//End halo increments + }//End loop through all tasks + + }// nhaloout > 0 + + //If this is the master task (writer task) close the file (and other objects) + if(ThisTask == masterTask) + { + //For HDF5 output + if (All.OutputFormat == 2) + { //Check if libraries have been included +#ifdef HDF5_SUPPORT + if (nhaloout > 0) + { // Close the dataset + hdf5_status = H5Dclose(hdf5_dataset); + // Close the dataset + hdf5_status = H5Pclose(hdf5_properties); + // Close the data space + hdf5_status = H5Sclose(hdf5_dataspace_in_file); + } + // Close the file type + hdf5_status = H5Tclose(hdf5_ftype); + // Close the data type + hdf5_status = H5Tclose(hdf5_dtype); + //Close the file and print to screen if writing was not successful + hdf5_status = H5Fclose(gal_file); + if (hdf5_status != 0) printf("%s Task %5d failed to write to file %s\n",All.startline,ThisTask,buf); +#endif + }//End HDF5 + //Otherwise close the ascii file + else + { //Close the file + fclose(fd); + }//End ascii + }//End master/writer task + + }//End tasks in the group + + }//End output redshifts + + //Set MPI barrier so that only one task writes to a given file + MPI_Barrier(MPI_COMM_WORLD); + }//End loop through all groups + + //Free the CommBuffer + efree(CommBuffer); + +} +#endif + + +#ifdef WRITE_HALO_CATALOG +/*! \brief This function collects all halo data and prints it + * + * This function collects the halo data from all tasks and writes it to All.NumOutputFiles files. There + * are All.NumFilesInParallel files written in parallel. Each master task collects the halo data from + * its slave tasks and writes it. If there is one output file it will be called haloes.out otherwise there + * will be n files called haloes.i where i < n. + */ +void output_halos(void) +{ + int i, j, k, ihalo, iprog, filenr, masterTask, lastTask, ngroups, maxlen, task; + int left_to_send, p, send_this_turn, offset, nhaloout; + double amhalf; + char buf[NSTRING]; + FILE *fd; + MPI_Status status; + + //Structure that stores all halo/galaxy properties that will be saved + struct haloout { + float mvir,mdot,mpeak,ampeak,amhalf,rvir,c,l,x,y,z,u,v,w; + unsigned short t; + long long hid, did, uid; + } *hp, hsend; + +#ifdef HDF5_SUPPORT + hid_t halo_file, hdf5_dataspace_memory, hdf5_dataspace_in_file, hdf5_dtype, hdf5_ftype, hdf5_filespace; + hid_t hdf5_properties, hdf5_dataset; + hid_t hdf5_att_space, hdf5_att_redshift, hdf5_att_lbox, hdf5_att_hubble; + hid_t hdf5_att_omega0, hdf5_att_omegal, hdf5_att_omegab; + herr_t hdf5_status; + hsize_t dims[1], maxdims[1], count[1], start[1]; + int sendsum = 0; + double tmp; + char path[NSTRING]; +#endif + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Writing haloes in 1st universe to %d files...\n", All.fullline,All.startline,All.NumOutputFiles * All.Noutputredshifts); + + //Check if there are enough tasks to write all output files + if (All.NTaskPerUniverse < All.NumOutputFiles && ThisTask == 0) + endrun("Fatal error. Number of processors must be larger or equal than All.NumOutputFiles."); + + //Allocate CommBuffer + CommBuffer = emalloc("CommBuffer", All.BufferSize * 1024 * 1024); + + //Get maximum number of haloes that fit in the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / sizeof(struct halo); + + //Assign processors to output files + distribute_file(All.NTaskPerUniverse, All.NumOutputFiles, 0, 0, All.NTaskPerUniverse - 1, &filenr, &masterTask, &lastTask); + + //Go through all output redshifts + for(i = 0; i < All.Noutputredshifts; i++) + { + //If the output format has been set to HDF5 + if (All.OutputFormat == 2) + { + //Check if HDF5 libraries are set +#ifdef HDF5_SUPPORT + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/haloes.S%d.%d.h5", All.OutputDir, Output_iscale[i], filenr); + else + sprintf(buf, "%s/haloes.S%d.h5", All.OutputDir, Output_iscale[i]); + //If not return +#else + if (ThisTask == 0) printf("%s Output format has been set to 2 but HDF5 support was not enabled.\n",All.startline); + return; +#endif + } + //Otherwise use standard ascii files + else + { + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/haloes.S%d.%d", All.OutputDir, Output_iscale[i], filenr); + else + sprintf(buf, "%s/haloes.S%d.out", All.OutputDir, Output_iscale[i]); + } + + //Get number of groups + ngroups = All.NumOutputFiles / All.NumFilesInParallel; + if((All.NumOutputFiles % All.NumFilesInParallel)) ngroups++; + + //For each group do... + for(j = 0; j < ngroups; j++) + { + //This task will be processed now + if((filenr / All.NumFilesInParallel) == j && MasterTask == 0) + { //The masterTask opens the file + if(ThisTask == masterTask) + { //If the hdf5 output format has been selected + if (All.OutputFormat == 2) + { //Check if the libraries have been included +#ifdef HDF5_SUPPORT + //Create hdf5 file + halo_file = H5Fcreate(buf, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + //Print to screen + printf("%s Writing output file at z = %f: '%s' (file %d of %d)\n",All.startline, OutputRedshifts[i], buf, filenr+1, All.NumOutputFiles); + //Specify data type + hdf5_dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct haloout)); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass", HOFFSET(struct haloout, mvir), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_growth_rate", HOFFSET(struct haloout, mdot), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass_peak", HOFFSET(struct haloout, mpeak), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_peak_mass", HOFFSET(struct haloout, ampeak), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_half_mass", HOFFSET(struct haloout, amhalf), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_radius", HOFFSET(struct haloout, rvir), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Concentration", HOFFSET(struct haloout, c), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_spin", HOFFSET(struct haloout, l), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_pos", HOFFSET(struct haloout, x), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_pos", HOFFSET(struct haloout, y), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_pos", HOFFSET(struct haloout, z), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_vel", HOFFSET(struct haloout, u), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_vel", HOFFSET(struct haloout, v), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_vel", HOFFSET(struct haloout, w), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Type", HOFFSET(struct haloout, t), H5T_NATIVE_USHORT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_ID", HOFFSET(struct haloout, hid), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Desc_ID", HOFFSET(struct haloout, did), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Up_ID", HOFFSET(struct haloout, uid), H5T_NATIVE_LLONG); + //Specify file type + k = 0; + hdf5_ftype = H5Tcreate(H5T_COMPOUND, 4 * 14 + 2 + 8 * 3); + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_growth_rate", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass_peak", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_peak_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_half_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_radius", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Concentration", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_spin", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Type", k, H5T_STD_U16LE); k+=2; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Desc_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Up_ID", k, H5T_STD_I64LE); k+=8; +#endif + } + //Otherwise open standard ascii file + else + { //Open file + if(!(fd = fopen(buf, "w"))) + { //If not possible print to screen and abort + printf("%s Can't open file `%s' for writing output.\n",All.startline,buf); + endrun("file open error"); + } + //If file can be opened print to screen + printf("%s Writing output file at z = %f: '%s' (file %d of %d)\n",All.startline, OutputRedshifts[i], buf, filenr+1, All.NumOutputFiles); + //Write ascii header + task = 0; + fprintf(fd,"#Halo_mass(%d)",task); task++; + fprintf(fd," Halo_growth_rate(%d)",task); task++; + fprintf(fd," Halo_mass_peak(%d)",task); task++; + fprintf(fd," Scale_peak_mass(%d)",task); task++; + fprintf(fd," Scale_half_mass(%d)",task); task++; + fprintf(fd," Halo_radius(%d)",task); task++; + fprintf(fd," Concentration(%d)",task); task++; + fprintf(fd," Halo_spin(%d)",task); task++; + fprintf(fd," X_pos(%d)",task); task++; + fprintf(fd," Y_pos(%d)",task); task++; + fprintf(fd," Z_pos(%d)",task); task++; + fprintf(fd," X_vel(%d)",task); task++; + fprintf(fd," Y_vel(%d)",task); task++; + fprintf(fd," Z_vel(%d)",task); task++; + fprintf(fd," Type(%d)",task); task++; + fprintf(fd," Halo_ID(%d)",task); task++; + fprintf(fd," Desc_ID(%d)",task); task++; + fprintf(fd," Up_ID(%d)",task); task++; + fprintf(fd,"\n"); + fprintf(fd,"#Emerge Branch: %s\n",BRANCH); + fprintf(fd,"#Emerge Version: %s\n",VERSION); + fprintf(fd,"#Model Name: %s\n",All.model_name); + fprintf(fd,"#Scale Factor: %f\n",ScaleFactor[Output_iscale[i]]); + fprintf(fd,"#Box Size: %f (Mpc)\n",All.Lbox*All.x_unit); + fprintf(fd,"#Hubble Parameter: %f\n",All.h_100); + fprintf(fd,"#Omega_0: %f\n",All.Omega_0); + fprintf(fd,"#Omega_Lambda: %f\n",All.Omega_Lambda_0); + fprintf(fd,"#Omega_Baryon: %f\n",All.Omega_Baryon_0); + fprintf(fd,"#Halo_mass: Current virial mass of the halo (log Msun).\n"); + fprintf(fd,"#Halo_growth_rate: Growth rate of the halo (Msun/yr)\n"); + fprintf(fd,"#Halo_mass_peak: Peak virial mass of the halo through its history (log Msun).\n"); + fprintf(fd,"#Scale_peak_mass: Scale Factor when halo had its peak mass.\n"); + fprintf(fd,"#Scale_half_mass: Scale Factor when halo first had half of its peak mass.\n"); + fprintf(fd,"#Halo_radius: Current virial radius of the halo (kpc).\n"); + fprintf(fd,"#Concentration: Virial radius divided by halo scale length.\n"); + fprintf(fd,"#Halo_spin: Halo spin parameter.\n"); + fprintf(fd,"#X_pos: Comoving halo position along the X axis (Mpc).\n"); + fprintf(fd,"#Y_pos: Comoving halo position along the Y axis (Mpc).\n"); + fprintf(fd,"#Z_pos: Comoving halo position along the Z axis (Mpc).\n"); + fprintf(fd,"#X_vel: Peculiar/physical velocity of the halo along the X axis (km/s).\n"); + fprintf(fd,"#Y_vel: Peculiar/physical velocity of the halo along the Y axis (km/s).\n"); + fprintf(fd,"#Z_vel: Peculiar/physical velocity of the halo along the Z axis (km/s).\n"); + fprintf(fd,"#Type: Whether the halo is a main halo (0), subhalo (1), or lost halo with orphan galaxy (2).\n"); + fprintf(fd,"#Halo_ID: Halo ID that corresponds to the ID in the original halo catalogue.\n"); + fprintf(fd,"#Desc_ID: ID of the descendant halo. If there is none then Desc_ID = -1.\n"); + fprintf(fd,"#Up_ID: ID of the most massive halo. If this is a main halo then Up_ID = -1.\n"); + } + } + + //Determine number of systems that will be printed on each task (depending on redshift and mass) + nhaloout = 0; + for (ihalo = 0; ihalo < Nhalos; ihalo++) + if (H[ihalo].iscale == Output_iscale[i] && H[ihalo].gone == 0) nhaloout++; + + if (nhaloout > 0) + { + +#ifdef HDF5_SUPPORT + if (ThisTask == masterTask && All.OutputFormat == 2) + { + //Set the path name for the halo data set + sprintf(path,"/Haloes"); + //Initial size is the number of haloes on the master task + dims[0] = nhaloout; + //Allow for variable total size + maxdims[0] = H5S_UNLIMITED; + //Create the data space + hdf5_dataspace_in_file = H5Screate_simple(1, dims, maxdims); + //Create the data properties + hdf5_properties = H5Pcreate(H5P_DATASET_CREATE); + hdf5_status = H5Pset_chunk(hdf5_properties, 1, dims); // set chunk size + hdf5_status = H5Pset_shuffle(hdf5_properties); // reshuffle bytes to get better compression ratio + hdf5_status = H5Pset_deflate(hdf5_properties, 9); // gzip compression level 9 + hdf5_status = H5Pset_fletcher32(hdf5_properties); // Fletcher32 checksum on dataset + //If filters could be set use them to create the data set + if(H5Pall_filters_avail(hdf5_properties)) + hdf5_dataset = H5Dcreate(halo_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, hdf5_properties, H5P_DEFAULT); + //Otherwise create the default data set + else + hdf5_dataset = H5Dcreate(halo_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Initialise the number of haloes in the set to zero + sendsum = 0; + //Define Scalar for Attributes + hdf5_att_space = H5Screate(H5S_SCALAR); + //Write Redshift as attribute to dataset + hdf5_att_redshift = H5Acreate(hdf5_dataset, "Scale Factor", H5T_NATIVE_FLOAT, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_redshift, H5T_IEEE_F32LE, &ScaleFactor[Output_iscale[i]]); + H5Aclose(hdf5_att_redshift); + //Write Box Size as attribute to dataset + tmp = All.Lbox*All.x_unit; + hdf5_att_lbox = H5Acreate(hdf5_dataset, "Box Size", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_lbox, H5T_IEEE_F64LE, &tmp); + H5Aclose(hdf5_att_lbox); + //Write Hubble parameter as attribute to dataset + hdf5_att_hubble = H5Acreate(hdf5_dataset, "Hubble Parameter", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_hubble, H5T_IEEE_F64LE, &All.h_100); + H5Aclose(hdf5_att_hubble); + //Write Omega_0 as attribute to dataset + hdf5_att_omega0 = H5Acreate(hdf5_dataset, "Omega_0", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omega0, H5T_IEEE_F64LE, &All.Omega_0); + H5Aclose(hdf5_att_omega0); + //Write Omega_Lambda as attribute to dataset + hdf5_att_omegal = H5Acreate(hdf5_dataset, "Omega_Lambda", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omegal, H5T_IEEE_F64LE, &All.Omega_Lambda_0); + H5Aclose(hdf5_att_omegal); + //Write Omega_Baryon as attribute to dataset + hdf5_att_omegab = H5Acreate(hdf5_dataset, "Omega_Baryon", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_omegab, H5T_IEEE_F64LE, &All.Omega_Baryon_0); + H5Aclose(hdf5_att_omegab); + } +#endif + + //Now go through all tasks that are member of this group + for(task = masterTask, offset = 0; task <= lastTask; task++) + { + //if this task is processed + if(task == ThisTask) + { + //We need to get those haloes + left_to_send = nhaloout; + + //Tell this to the other tasks + for(p = masterTask; p <= lastTask; p++) + if(p != ThisTask) + MPI_Send(&left_to_send, 1, MPI_INT, p, TAG_NHALOS, MPI_COMM_WORLD); + } + //Reveive the number of haloes from active task + else MPI_Recv(&left_to_send, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD, &status); + + //Now send the haloes in increments + while(left_to_send > 0) + { + //Compute how many haloes we can send this turn + send_this_turn = left_to_send; + //If they do not all fit into the CommBuffer set to the maximum + if(send_this_turn > maxlen) send_this_turn = maxlen; + + //Sending task + if(ThisTask == task) + { + //Use the haloout struct for the CommBuffer + hp = (struct haloout *) CommBuffer; + //Initialise the number of systems sent to zero + ihalo = 0; + //Go through all systems until the maximum that can be sent + while (ihalo < send_this_turn) + { //If we are at the right scale factor and the halo is not gone send it to the master + if (H[offset].iscale == Output_iscale[i] && H[offset].gone == 0) + { + //Find scale factor when virial mass was half of current value + amhalf = ScaleFactor[H[H[offset].impeak].iscale]; + iprog = H[H[offset].impeak].iprog; + if (iprog >=0 ) + { + while (H[iprog].mvir > 0.5 * H[H[offset].impeak].mvir) + { + if (H[iprog].iprog < 0) break; + iprog = H[iprog].iprog; + } + amhalf = ScaleFactor[H[iprog].iscale]; + } + + //Set all halo properties + hsend.mvir = log10(H[offset].mvir*All.m_unit); + hsend.mdot = H[offset].mdotbary*All.m_unit/All.t_unit/All.f_baryon; + hsend.mpeak = log10(H[H[offset].impeak].mvir*All.m_unit); + hsend.ampeak = ScaleFactor[H[H[offset].impeak].iscale]; + hsend.amhalf = amhalf; + hsend.rvir = H[offset].rvir*All.x_unit*1.e3; + hsend.c = H[offset].c; + hsend.l = H[offset].lambda; + hsend.x = H[offset].pos[0]; + hsend.y = H[offset].pos[1]; + hsend.z = H[offset].pos[2]; + hsend.u = H[offset].vel[0]; + hsend.v = H[offset].vel[1]; + hsend.w = H[offset].vel[2]; + hsend.t = H[offset].type; + hsend.hid = (long long)(H[offset].haloid)-1; + hsend.did = (long long)(H[offset].descid)-1; + hsend.uid = (long long)(H[offset].upid)-1; + + //Write this system to the CommBuffer + *hp++ = hsend; + //Increment the number of systems that are being sent + ihalo++; + } + //Increment the halo index + offset++; + } + } + + //Receive haloes + if(ThisTask == masterTask && task != masterTask) MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD, &status); + + //Send haloes + if(ThisTask != masterTask && task == ThisTask) MPI_Ssend(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, masterTask, TAG_HDATA, MPI_COMM_WORLD); + + //Collect haloes and write to file + if(ThisTask == masterTask) + { + //If the output format is hdf5 + if (All.OutputFormat == 2) + { +#ifdef HDF5_SUPPORT + //Set the starting index and the count + start[0] = sendsum; + count[0] = send_this_turn; + //Add the number of haloes that are sent this turn to the total number and set the size to the total + sendsum += send_this_turn; + dims[0] = sendsum; + //If the total size is larger than the initial set size for the data set extend it + if (sendsum > nhaloout) hdf5_status = H5Dset_extent(hdf5_dataset, dims); + //Get the data space of the data set + hdf5_filespace = H5Dget_space(hdf5_dataset); + //Select the hyperslab that corresponds to the current offset + hdf5_status = H5Sselect_hyperslab(hdf5_filespace, H5S_SELECT_SET, start, NULL, count, NULL); + //Set the size to the number of systems sent this turn + dims[0] = send_this_turn; + //Create a data space for the memory + hdf5_dataspace_memory = H5Screate_simple(1, dims, NULL); + //Write the data set + hdf5_status = H5Dwrite(hdf5_dataset, hdf5_dtype, hdf5_dataspace_memory, hdf5_filespace, H5P_DEFAULT, CommBuffer); + //Close the data space of the data set + H5Sclose(hdf5_filespace); + //Close the data space for the memory + H5Sclose(hdf5_dataspace_memory); +#endif + }//End HDF5 output + //Otherwise simply write the properties to an ascii file + else + { //Use the haloout struct for the CommBuffer + hp = (struct haloout *) CommBuffer; + //Go through all systems that were sent this turn + for (ihalo = 0; ihalo < send_this_turn; ihalo++) + { //Write a new line in the ascii file + fprintf(fd,"%f %e %f %f %f %f %f %f %f %f %f %f %f %f %hu %lld %lld %lld\n", + hp[ihalo].mvir, + hp[ihalo].mdot, + hp[ihalo].mpeak, + hp[ihalo].ampeak, + hp[ihalo].amhalf, + hp[ihalo].rvir, + hp[ihalo].c, + hp[ihalo].l, + hp[ihalo].x, + hp[ihalo].y, + hp[ihalo].z, + hp[ihalo].u, + hp[ihalo].v, + hp[ihalo].w, + hp[ihalo].t, + hp[ihalo].hid, + hp[ihalo].did, + hp[ihalo].uid); + }//End loop through all haloes + }//End ascii output + }//End master task (writer task) + //Decrease the number of haloes left to send by the number sent this turn + left_to_send -= send_this_turn; + }//End halo increments + }//End loop through all tasks + + }// nhaloout > 0 + + //If this is the master task (writer task) close the file (and other objects) + if(ThisTask == masterTask) + { + //For HDF5 output + if (All.OutputFormat == 2) + { //Check if libraries have been included +#ifdef HDF5_SUPPORT + if (nhaloout > 0) + { // Close the dataset + hdf5_status = H5Dclose(hdf5_dataset); + // Close the dataset + hdf5_status = H5Pclose(hdf5_properties); + // Close the data space + hdf5_status = H5Sclose(hdf5_dataspace_in_file); + } + // Close the file type + hdf5_status = H5Tclose(hdf5_ftype); + // Close the data type + hdf5_status = H5Tclose(hdf5_dtype); + //Close the file and print to screen if writing was not successful + hdf5_status = H5Fclose(halo_file); + if (hdf5_status != 0) printf("%s Task %5d failed to write to file %s\n",All.startline,ThisTask,buf); +#endif + }//End HDF5 + //Otherwise close the ascii file + else + { //Close the file + fclose(fd); + }//End ascii + }//End master/writer task + + }//End tasks in the group + + }//End output redshifts + + //Set MPI barrier so that only one task writes to a given file + MPI_Barrier(MPI_COMM_WORLD); + }//End loop through all groups + + //Free the CommBuffer + efree(CommBuffer); + +} +#endif + + +#ifdef WRITE_MAINBRANCH +/*! \brief This function prints the main branch of galaxies with a specified halo or stellar mass at a specified redshift + * + * This function checks each tree is the main progenitor has a specific stellar or halo mass at a specific + * redshift. If the tree fulfils this criterion it is sent to the master task and then written to a file. + * Output can be generated in either ascii format, where main branch data is written in blocks, or in HDF5 + * format, where main branch data sets are stored individually in groups according to their mass bin. + * There are a total of All.NumOutputFiles files and All.NumFilesInParallel files are written in parallel. + * Each master task collects the halo data from its slave tasks and writes it. If there is one output file + * it will be called mainbranches.out otherwise there will be n files called mainbranches.i where i < n. + */ +void output_mainbranch(void) +{ + + int i, j, k, filenr, masterTask, lastTask, ngroups, task; + int iroot, ihalo, iprog, present_at_z, Nmainbranch, Nrootloop, mbflag, imb, Nroot; + int *roothalos; + double mass; + char buf[NSTRING]; + FILE *fd; + MPI_Status status; + + //Structure that stores all halo/galaxy properties that will be saved + struct mbout { + float a,mh,mdot,mhp,amp,r,c,l,ms,sfr,icm,x,y,z,u,v,w; + unsigned short t; + long long hid, did, uid; + } *mainbranch, mb; + +#ifdef HDF5_SUPPORT + hid_t mb_file, hdf5_dtype, hdf5_ftype, set_group; + hid_t hdf5_properties, hdf5_dataset, hdf5_dataspace_in_file, atype; + hid_t hdf5_att_space, hdf5_att_redshift, hdf5_att_massbin, hdf5_att_binsize, hdf5_att_masstag; + herr_t hdf5_status; + hsize_t dims[1], maxdims[1]; + int asize; + char path[NSTRING], masstag[NSTRING]; +#endif + + //Find number of root haloes (at z=0) for each task + Nroot = 0; + for (ihalo = 0; ihalo < Nhalos; ihalo++) + if (H[ihalo].iscale == All.NTimesteps - 1) + Nroot++; + + //Allocate memory for the array containing indices of root haloes + roothalos = (int *) emalloc_movable(&roothalos, "RootHalos", Nroot *sizeof(int)); + + //Fill array containing indices of root haloes + iroot = 0; + for (ihalo = 0; ihalo < Nhalos; ihalo++) + { //Each halo at z=0 is a root halo (also orphans) + if (H[ihalo].iscale == All.NTimesteps - 1) + { //Fill array + roothalos[iroot] = ihalo; + iroot++; + } + } + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Writing main branches in 1st universe to %d files...\n", All.fullline,All.startline,All.NumOutputFiles); + + //Check if there are enough tasks to write all output files + if (All.NTaskPerUniverse < All.NumOutputFiles && ThisTask == 0) + endrun("Fatal error. Number of processors must be larger or equal than All.NumOutputFiles."); + + //Assign processors to output files + distribute_file(All.NTaskPerUniverse, All.NumOutputFiles, 0, 0, All.NTaskPerUniverse - 1, &filenr, &masterTask, &lastTask); + + //If the output format has been set to HDF5 + if (All.OutputFormat == 2) + { + //Check if HDF5 libraries are set +#ifdef HDF5_SUPPORT + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/mainbranches.S%d.%d.h5", All.OutputDir, All.MainBranch_iscale, filenr); + else + sprintf(buf, "%s/mainbranches.S%d.h5", All.OutputDir, All.MainBranch_iscale); + //If not return +#else + if (ThisTask == 0) printf("%s Output format has been set to 2 but HDF5 support was not enabled.\n",All.startline); + return; +#endif + } + //Otherwise use standard ascii files + else + { + if (All.NumOutputFiles > 1) + sprintf(buf, "%s/mainbranches.S%d.%d", All.OutputDir, All.MainBranch_iscale, filenr); + else + sprintf(buf, "%s/mainbranches.S%d.out", All.OutputDir, All.MainBranch_iscale); + } + + //Get number of groups + ngroups = All.NumOutputFiles / All.NumFilesInParallel; + if((All.NumOutputFiles % All.NumFilesInParallel)) ngroups++; + + //For each group do... + for(j = 0; j < ngroups; j++) + { + //This task will be processed now + if((filenr / All.NumFilesInParallel) == j && MasterTask == 0) + { //The masterTask opens the file + if(ThisTask == masterTask) + { //If the hdf5 output format has been selected + if (All.OutputFormat == 2) + { //Check if the libraries have been included +#ifdef HDF5_SUPPORT + //Create hdf5 file + mb_file = H5Fcreate(buf, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + //Print to screen + printf("%s Writing main branch for galaxies at z = %3.2f: '%s' (file %d of %d)\n",All.startline, All.mainBranchRedshift, buf, filenr+1, All.NumOutputFiles); + //Specify data type + hdf5_dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct mbout)); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_factor", HOFFSET(struct mbout, a), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass", HOFFSET(struct mbout, mh), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_growth_rate", HOFFSET(struct mbout, mdot), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_mass_peak", HOFFSET(struct mbout, mhp), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Scale_peak_mass", HOFFSET(struct mbout, amp), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_radius", HOFFSET(struct mbout, r), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Concentration", HOFFSET(struct mbout, c), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_spin", HOFFSET(struct mbout, l), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Stellar_mass", HOFFSET(struct mbout, ms), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "SFR", HOFFSET(struct mbout, sfr), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Intra_cluster_mass", HOFFSET(struct mbout, icm), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_pos", HOFFSET(struct mbout, x), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_pos", HOFFSET(struct mbout, y), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_pos", HOFFSET(struct mbout, z), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "X_vel", HOFFSET(struct mbout, u), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Y_vel", HOFFSET(struct mbout, v), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Z_vel", HOFFSET(struct mbout, w), H5T_NATIVE_FLOAT); + hdf5_status = H5Tinsert(hdf5_dtype, "Type", HOFFSET(struct mbout, t), H5T_NATIVE_USHORT); + hdf5_status = H5Tinsert(hdf5_dtype, "Halo_ID", HOFFSET(struct mbout, hid), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Desc_ID", HOFFSET(struct mbout, did), H5T_NATIVE_LLONG); + hdf5_status = H5Tinsert(hdf5_dtype, "Up_ID", HOFFSET(struct mbout, uid), H5T_NATIVE_LLONG); + //Specify file type + k = 0; + hdf5_ftype = H5Tcreate(H5T_COMPOUND, 4 * 17 + 2 + 8 * 3); + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_factor", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_growth_rate", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_mass_peak", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Scale_peak_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_radius", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Concentration", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_spin", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Stellar_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "SFR", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Intra_cluster_mass", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_pos", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "X_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Y_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Z_vel", k, H5T_IEEE_F32LE); k+=4; + hdf5_status = H5Tinsert(hdf5_ftype, "Type", k, H5T_STD_U16LE); k+=2; + hdf5_status = H5Tinsert(hdf5_ftype, "Halo_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Desc_ID", k, H5T_STD_I64LE); k+=8; + hdf5_status = H5Tinsert(hdf5_ftype, "Up_ID", k, H5T_STD_I64LE); k+=8; + //Add attribute space to file + hdf5_att_space = H5Screate(H5S_SCALAR); + //Write selection redshift as attribute to file + hdf5_att_redshift = H5Acreate(mb_file, "Selection_Redshift", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_redshift, H5T_IEEE_F64LE, &All.mainBranchRedshift); + H5Aclose(hdf5_att_redshift); + //Close attribute space + H5Sclose(hdf5_att_space); +#endif + } + //Otherwise open standard ascii file + else + { //Open file + if(!(fd = fopen(buf, "w"))) + { //If not possible print to screen and abort + printf("%s Can't open file `%s' for writing output.\n",All.startline,buf); + endrun("file open error"); + } + //If file can be opened print to screen + printf("%s Writing main branch for galaxies at z = %3.2f: '%s' (file %d of %d)\n",All.startline, All.mainBranchRedshift, buf, filenr+1, All.NumOutputFiles); + //Print column names + task = 0; + fprintf(fd,"#Scale_factor(%d)",task); task++; + fprintf(fd," Halo_mass(%d)",task); task++; + fprintf(fd," Halo_growth_rate(%d)",task); task++; + fprintf(fd," Halo_mass_peak(%d)",task); task++; + fprintf(fd," Scale_peak_mass(%d)",task); task++; + fprintf(fd," Halo_radius(%d)",task); task++; + fprintf(fd," Concentration(%d)",task); task++; + fprintf(fd," Halo_spin(%d)",task); task++; + fprintf(fd," Stellar_mass(%d)",task); task++; + fprintf(fd," SFR(%d)",task); task++; + fprintf(fd," Intra_cluster_mass(%d)",task); task++; + fprintf(fd," X_pos(%d)",task); task++; + fprintf(fd," Y_pos(%d)",task); task++; + fprintf(fd," Z_pos(%d)",task); task++; + fprintf(fd," X_vel(%d)",task); task++; + fprintf(fd," Y_vel(%d)",task); task++; + fprintf(fd," Z_vel(%d)",task); task++; + fprintf(fd," Type(%d)",task); task++; + fprintf(fd," Halo_ID(%d)",task); task++; + fprintf(fd," Desc_ID(%d)",task); task++; + fprintf(fd," Up_ID(%d)",task); task++; + fprintf(fd,"\n"); + fprintf(fd,"#Emerge Branch: %s\n",BRANCH); + fprintf(fd,"#Emerge Version: %s\n",VERSION); + fprintf(fd,"#Model Name: %s\n",All.model_name); + fprintf(fd,"#Box Size: %f (Mpc)\n",All.Lbox*All.x_unit); + fprintf(fd,"#Hubble Parameter: %f\n",All.h_100); + fprintf(fd,"#Omega_0: %f\n",All.Omega_0); + fprintf(fd,"#Omega_Lambda: %f\n",All.Omega_Lambda_0); + fprintf(fd,"#Omega_Baryon: %f\n",All.Omega_Baryon_0); + //Write a header including the selection redshift to the first line + fprintf(fd,"#This file contains all main branches with a mass of %s at z = %4.2f\n",All.output_mass_mb,All.mainBranchRedshift); + //Add which mass has been used for selecting the trees + if (All.MainBranchMassType == 1) fprintf(fd,"#Stellar mass has been used for this selection.\n"); + else fprintf(fd,"# Halo mass has been used for this selection.\n"); + fprintf(fd,"#Halo_mass: Current virial mass of the halo (log Msun).\n"); + fprintf(fd,"#Halo_growth_rate: Growth rate of the halo (Msun/yr)\n"); + fprintf(fd,"#Halo_mass_peak: Peak virial mass of the halo through its history (log Msun).\n"); + fprintf(fd,"#Scale_peak_mass: Scale Factor when halo had its peak mass.\n"); + fprintf(fd,"#Halo_radius: Current virial radius of the halo (kpc).\n"); + fprintf(fd,"#Concentration: Virial radius divided by halo scale length.\n"); + fprintf(fd,"#Halo_spin: Halo spin parameter.\n"); + fprintf(fd,"#Stellar_mass: Intrinsic stellar mass of the galaxy (log Msun).\n"); + fprintf(fd,"#SFR: Star formation rate of the galaxy (Msun/yr).\n"); + fprintf(fd,"#Intra_cluster_mass: Stellar mass in the halo (log Msun).\n"); + fprintf(fd,"#X_pos: Comoving galaxy position along the X axis (Mpc).\n"); + fprintf(fd,"#Y_pos: Comoving galaxy position along the Y axis (Mpc).\n"); + fprintf(fd,"#Z_pos: Comoving galaxy position along the Z axis (Mpc).\n"); + fprintf(fd,"#X_vel: Peculiar/physical velocity of the galaxy along the X axis (km/s).\n"); + fprintf(fd,"#Y_vel: Peculiar/physical velocity of the galaxy along the Y axis (km/s).\n"); + fprintf(fd,"#Z_vel: Peculiar/physical velocity of the galaxy along the Z axis (km/s).\n"); + fprintf(fd,"#Type: Whether the galaxy is a central (0), satellite (1), or orphan (2).\n"); + fprintf(fd,"#Halo_ID: Halo ID that corresponds to the ID in the original halo catalogue.\n"); + fprintf(fd,"#Desc_ID: ID of the descendant halo. If there is none then Desc_ID = -1.\n"); + fprintf(fd,"#Up_ID: ID of the most massive halo. If this is a main halo then Up_ID = -1.\n"); + //Print an empty line + fprintf(fd,"##################################################\n\n"); + }//End ascii + }//End master task + + // Loop over all selected mass bins for the main branch output + for (i = 0; i < All.Noutputbranch; i++) + { //If this is the master task start with a new group + if (ThisTask == masterTask) + { //If we write an HDF5 file, create a group and write the bin's mass and size, and a tag which mass has been used as an attribute + if (All.OutputFormat == 2) + { +#ifdef HDF5_SUPPORT + //Set the path name for the mass bin + sprintf(path,"/MainBranch_M%03d",i); + //Create new group for the SMFs + set_group = H5Gcreate(mb_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Add attribute space to group + hdf5_att_space = H5Screate(H5S_SCALAR); + //Write mass bin as attribute to group + hdf5_att_massbin = H5Acreate(set_group, "Mass_bin", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + mass = (double)(MainBranchMasses[i]); + H5Awrite(hdf5_att_massbin, H5T_IEEE_F64LE, &mass); + H5Aclose(hdf5_att_massbin); + //Write mass bin size as attribute to group + hdf5_att_binsize = H5Acreate(set_group, "Mass_bin_size", H5T_NATIVE_DOUBLE, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + mass = (double)(MainBranchBinSize[i]); + H5Awrite(hdf5_att_binsize, H5T_IEEE_F64LE, &mass); + H5Aclose(hdf5_att_binsize); + //Write mass tag as attribute to group + atype = H5Tcopy(H5T_C_S1); + //Select which mass has been used + if (All.MainBranchMassType == 1) + { //For stellar mass we need 12 characters + asize = 12; + sprintf(masstag, "Stellar_Mass"); + } + else + { //For halo mass we need 9 characters + asize = 9; + sprintf(masstag, "Halo_Mass"); + } + //Set size + H5Tset_size(atype, asize); + H5Tset_strpad(atype,H5T_STR_NULLTERM); + hdf5_att_masstag = H5Acreate(set_group, "Mass_Type", atype, hdf5_att_space, H5P_DEFAULT, H5P_DEFAULT); + H5Awrite(hdf5_att_masstag, atype, masstag); + H5Aclose(hdf5_att_masstag); + //Close String Type + H5Tclose(atype); + //Close attribute space + H5Sclose(hdf5_att_space); +#endif + } + else //If we use an ascii file + { //Print the mass and width of this bin + fprintf(fd,"# The following trees are for masses of %7.5f with a bin size of %7.5f\n",MainBranchMasses[i],MainBranchBinSize[i]); + //Print line + fprintf(fd,"##################################################\n"); + } + } //Done for the master task + + //Allocate memory for one main branch over the full possible number of snapshots + mainbranch = emalloc_movable(&mainbranch,"Mainbranch", All.NTimesteps * sizeof(struct mbout)); + //Set main branch index to 0 + imb = 0; + + //Now go through all tasks that are member of this group + for(task = masterTask; task <= lastTask; task++) + { //If this task is processed + if(task == ThisTask) + { //If we're beyond the master task, send number of root haloes to master task + if (ThisTask != masterTask) MPI_Ssend(&Nroot, 1, MPI_INT, masterTask, TAG_NTREES, MPI_COMM_WORLD); + //Go through all root haloes on this task + for (iroot = 0; iroot < Nroot; iroot++) + { //Initialise main branch flag to 0 + mbflag = 0; + //Set flag for presence at selected redshift to 1 + present_at_z = 1; + //Set iprog to root halo + iprog = roothalos[iroot]; //Loop until main branch progenitor is at selected redshift + while (H[iprog].iscale > All.MainBranch_iscale) + { //If progenitor does not exist + if (H[iprog].iprog < 0) + { //Set presence flag to 0 and break + present_at_z = 0; + break; + } + //Otherwise go to main progenitor + else iprog = H[iprog].iprog; + }//Done with looping over progenitors + //Select which mass is used for selection + if (All.MainBranchMassType == 1) mass = log10(H[iprog].mstar*All.m_unit); + else mass = log10(H[iprog].mvir*All.m_unit); + //If mass is in the bin and halo is still present + if (mass >= MainBranchMasses[i] - 0.5 * MainBranchBinSize[i] && + mass < MainBranchMasses[i] + 0.5 * MainBranchBinSize[i] && + present_at_z == 1) + { //System is relevant - so deal with it and set flag to 1 + mbflag = 1; + //Tell master task that this is a relevant tree + if (ThisTask != masterTask) MPI_Ssend(&mbflag, 1, MPI_INT, masterTask, TAG_COUNT, MPI_COMM_WORLD); + //Start again at z = 0 + iprog = roothalos[iroot]; + //Set counter for the number of entries in this tree to 0 + Nmainbranch = 0; + //Loop over progenitors until there is no more progenitor + while (H[iprog].iprog >= 0) + { //Set all properties for the mb structure + mb.a = H[iprog].a; + mb.mh = log10(H[iprog].mvir*All.m_unit); + mb.mdot = H[iprog].mdotbary*All.m_unit/All.t_unit/All.f_baryon; + mb.mhp = log10(H[H[iprog].impeak].mvir*All.m_unit); + mb.amp = ScaleFactor[H[H[iprog].impeak].iscale]; + mb.r = H[iprog].rvir*All.x_unit*1.e3; + mb.c = H[iprog].c; + mb.l = H[iprog].lambda; + mb.ms = log10(H[iprog].mstar*All.m_unit); + mb.sfr = H[iprog].sfr*All.m_unit/All.t_unit; + mb.icm = log10(H[iprog].icm*All.m_unit); + mb.x = H[iprog].pos[0]; + mb.y = H[iprog].pos[1]; + mb.z = H[iprog].pos[2]; + mb.u = H[iprog].vel[0]; + mb.v = H[iprog].vel[1]; + mb.w = H[iprog].vel[2]; + mb.t = H[iprog].type; + mb.hid = (long long)(H[iprog].haloid)-1; + mb.did = (long long)(H[iprog].descid)-1; + mb.uid = (long long)(H[iprog].upid)-1; + //Write it all to next main branch entry + mainbranch[Nmainbranch] = mb; + //Go to next main progenitor + iprog = H[iprog].iprog; + //And increment the counter + Nmainbranch++; + } //End loop through main branch + //If we're beyond the master task, send number of haloes in main branch and then send full main branch + if(ThisTask != masterTask) + { + MPI_Ssend(&Nmainbranch, 1, MPI_INT, masterTask, TAG_NHALOS, MPI_COMM_WORLD); + MPI_Ssend(mainbranch, (sizeof(struct mbout)) * Nmainbranch, MPI_BYTE, masterTask, TAG_HDATA, MPI_COMM_WORLD); + } + else //If this is the master task, write the main branch in a file + { //Check if we write an HDF5 file + if (All.OutputFormat == 2) + { +#ifdef HDF5_SUPPORT + //Set the path name for the halo data set + sprintf(path,"/MainBranch_M%03d/Tree_%07d",i,imb); + //Initial size is the number of haloes on the master task + dims[0] = Nmainbranch; + //Allow for variable total size + maxdims[0] = H5S_UNLIMITED; + //Create the data space + hdf5_dataspace_in_file = H5Screate_simple(1, dims, maxdims); + //Create the data properties + hdf5_properties = H5Pcreate(H5P_DATASET_CREATE); + hdf5_status = H5Pset_chunk(hdf5_properties, 1, dims); // set chunk size + hdf5_status = H5Pset_shuffle(hdf5_properties); // reshuffle bytes to get better compression ratio + hdf5_status = H5Pset_deflate(hdf5_properties, 9); // gzip compression level 9 + hdf5_status = H5Pset_fletcher32(hdf5_properties); // Fletcher32 checksum on dataset + //If filters could be set use them to create the data set + if(H5Pall_filters_avail(hdf5_properties)) + hdf5_dataset = H5Dcreate(mb_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, hdf5_properties, H5P_DEFAULT); + //Otherwise create the default data set + else + hdf5_dataset = H5Dcreate(mb_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Write main branch to data set + hdf5_status = H5Dwrite(hdf5_dataset, hdf5_dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, mainbranch); + //Close the dataset + hdf5_status = H5Dclose(hdf5_dataset); + //Close the properties + hdf5_status = H5Pclose(hdf5_properties); + //Close the data space + hdf5_status = H5Sclose(hdf5_dataspace_in_file); +#endif + } + else //Write main branch to the ascii file + { //First we write the index number of this main branch + fprintf(fd,"#%03d_%07d\n",i,imb); + //Then write all systems in the main branch + for (ihalo = 0; ihalo < Nmainbranch; ihalo++) + fprintf(fd, + "%f %f %e %f %f %f %f %f %f %e %f %f %f %f %f %f %f %hu %lld %lld %lld\n", + mainbranch[ihalo].a, + mainbranch[ihalo].mh, + mainbranch[ihalo].mdot, + mainbranch[ihalo].mhp, + mainbranch[ihalo].amp, + mainbranch[ihalo].r, + mainbranch[ihalo].c, + mainbranch[ihalo].l, + mainbranch[ihalo].ms, + mainbranch[ihalo].sfr, + mainbranch[ihalo].icm, + mainbranch[ihalo].x, + mainbranch[ihalo].y, + mainbranch[ihalo].z, + mainbranch[ihalo].u, + mainbranch[ihalo].v, + mainbranch[ihalo].w, + mainbranch[ihalo].t, + mainbranch[ihalo].hid, + mainbranch[ihalo].did, + mainbranch[ihalo].uid); + //And separate with an empty line + fprintf(fd,"\n"); + } //Done writing the main branch for master task + } //Done with the master task + //Increment index for the main branch in this mass bin + imb++; + } //Done with this selected system + else + //Tree does not have the right mass at the selected redshift + { //System is not relevant - set flag to 0 + mbflag = 0; + //Tell master task that this tree is not relevant + if (ThisTask != masterTask) MPI_Ssend(&mbflag, 1, MPI_INT, masterTask, TAG_COUNT, MPI_COMM_WORLD); + } + }//Done with the loop over all trees + }//Done with active task task + //Now deal with master task while other task is active + else if (ThisTask == masterTask && task != masterTask) + { //Receive number of root haloes on the active task + MPI_Recv(&Nrootloop, 1, MPI_INT, task, TAG_NTREES, MPI_COMM_WORLD, &status); + //Go through all root haloes in the active task + for (iroot = 0; iroot < Nrootloop; iroot++) + { //Receive the flag whether the halo is selected or not + MPI_Recv(&mbflag, 1, MPI_INT, task, TAG_COUNT, MPI_COMM_WORLD, &status); + //If this tree is relevant we need to print it + if (mbflag == 1) + { //Receive number of haloes in main branch and then full main branch + MPI_Recv(&Nmainbranch, 1, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD, &status); + MPI_Recv(mainbranch, (sizeof(struct mbout)) * Nmainbranch, MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD, &status); + //Now we need to print it, either to HDF5 or ascii - check if it's HDF5 first + if (All.OutputFormat == 2) + { //If format is set to HDF5 then write it to the current data group +#ifdef HDF5_SUPPORT + //Set the path name for the halo data set + sprintf(path,"/MainBranch_M%03d/Tree_%07d",i,imb); + //Initial size is the number of haloes on the master task + dims[0] = Nmainbranch; + //Allow for variable total size + maxdims[0] = H5S_UNLIMITED; + //Create the data space + hdf5_dataspace_in_file = H5Screate_simple(1, dims, maxdims); + //Create the data properties + hdf5_properties = H5Pcreate(H5P_DATASET_CREATE); + hdf5_status = H5Pset_chunk(hdf5_properties, 1, dims); // set chunk size + hdf5_status = H5Pset_shuffle(hdf5_properties); // reshuffle bytes to get better compression ratio + hdf5_status = H5Pset_deflate(hdf5_properties, 9); // gzip compression level 9 + hdf5_status = H5Pset_fletcher32(hdf5_properties); // Fletcher32 checksum on dataset + //If filters could be set use them to create the data set + if(H5Pall_filters_avail(hdf5_properties)) + hdf5_dataset = H5Dcreate(mb_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, hdf5_properties, H5P_DEFAULT); + //Otherwise create the default data set + else + hdf5_dataset = H5Dcreate(mb_file, path, hdf5_ftype, hdf5_dataspace_in_file, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Write main branch to data set + hdf5_status = H5Dwrite(hdf5_dataset, hdf5_dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, mainbranch); + //Close the dataset + hdf5_status = H5Dclose(hdf5_dataset); + //Close the dataset + hdf5_status = H5Pclose(hdf5_properties); + //Close the data space + hdf5_status = H5Sclose(hdf5_dataspace_in_file); +#endif + } + else //Write main branch to the ascii file + { //First we write the index number of this main branch + fprintf(fd,"#%03d_%07d\n",i,imb); + //Then write all systems in the main branch + for (ihalo = 0; ihalo < Nmainbranch; ihalo++) + fprintf(fd, + "%f %f %e %f %f %f %f %f %f %e %f %f %f %f %f %f %f %hu %lld %lld %lld\n", + mainbranch[ihalo].a, + mainbranch[ihalo].mh, + mainbranch[ihalo].mdot, + mainbranch[ihalo].mhp, + mainbranch[ihalo].amp, + mainbranch[ihalo].r, + mainbranch[ihalo].c, + mainbranch[ihalo].l, + mainbranch[ihalo].ms, + mainbranch[ihalo].sfr, + mainbranch[ihalo].icm, + mainbranch[ihalo].x, + mainbranch[ihalo].y, + mainbranch[ihalo].z, + mainbranch[ihalo].u, + mainbranch[ihalo].v, + mainbranch[ihalo].w, + mainbranch[ihalo].t, + mainbranch[ihalo].hid, + mainbranch[ihalo].did, + mainbranch[ihalo].uid); + //And separate with an empty line + fprintf(fd,"\n"); + } //Done writing the main branch for master task + //Increment index for the main branch in this mass bin + imb++; + }//Done with this relevant tree - now go to next tree + }//Loop over all trees on active task + }//Done with the master task while another task is active + }//End loop through all tasks + + //Free the memomry for the main branch structure + efree_movable(mainbranch); + + //If this is the master task (writer task) close the group +#ifdef HDF5_SUPPORT + if (ThisTask == masterTask && All.OutputFormat == 2) + { // Close set goup + hdf5_status = H5Gclose(set_group); + } +#endif + + }//Done with this mass bins - go to next + + //If this is the master task (writer task) close the file (and other objects) + if(ThisTask == masterTask) + { //For HDF5 output + if (All.OutputFormat == 2) + { //Check if libraries have been included +#ifdef HDF5_SUPPORT + // Close the file type + hdf5_status = H5Tclose(hdf5_ftype); + // Close the data type + hdf5_status = H5Tclose(hdf5_dtype); + //Close the file and print to screen if writing was not successful + hdf5_status = H5Fclose(mb_file); + if (hdf5_status != 0) printf("%s Task %5d failed to write to file %s\n",All.startline,ThisTask,buf); +#endif + }//End HDF5 + //Otherwise close the ascii file + else + { //Close the file + fclose(fd); + }//End ascii + }//End master/writer task + + }//End task in the group + + }//End groups + + //Free array with root haloes + efree_movable(roothalos); + +} +#endif diff --git a/src/proto.h b/src/proto.h new file mode 100644 index 0000000..8c8d69a --- /dev/null +++ b/src/proto.h @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File proto.h // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file proto.h +/// \brief Contains the declarations of all functions +/// +/// This file declares all functions in the code. All new functions should be added here as well. +/// The function headers should be declared in the block that corresponds to the file the function +/// is in. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef PROTO +#define PROTO +// +#include +#include +#include +#include +#include "allvars.h" + +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#include +#endif + + +//allocate.c +void malloc_init(void); +void report_detailed_memory_usage_of_largest_task(size_t * OldHighMarkBytes, const char *label, const char *func, const char *file, int line); +void dump_memory_table(void); +int dump_memory_table_buffer(char *p); +void *malloc_fullinfo(const char *varname, size_t n, const char *func, const char *file, int line); +void *malloc_movable_fullinfo(void *ptr, const char *varname, size_t n, const char *func, const char *file, int line); +void free_fullinfo(void *p, const char *func, const char *file, int line); +void free_movable_fullinfo(void *p, const char *func, const char *file, int line); +void *realloc_fullinfo(void *p, size_t n, const char *func, const char *file, int line); +void *realloc_movable_fullinfo(void *p, size_t n, const char *func, const char *file, int line); + +//clustering.c +int compute_wp(void); +int compute_wp_bin(int iwpbin, int Ngaltot, struct galaxy *localgal); +int collect_galaxies(int iwpbin, int Ngaltot, struct galaxy *localgal); +int construct_tree(struct kd_tree *tree, struct kd_pos *p, int count, int dim); +int find_nodes(struct kd_tree tree, int *nodeindex, int inode, int l, int level, int *counter, int save); +int partition(struct kd_pos *p, int left, int right, double pivotValue, int dim); +float mindist2_node_node(struct kd_node node1, struct kd_node node2, int dim); +float maxdist2_node_node(struct kd_node node1, struct kd_node node2, int dim); +int dualtree(struct kd_tree tree, struct kd_node node1, struct kd_node node2, struct kd_pos *p, float rmin2, float rmax2); +int tree_pairs(struct kd_tree tree, struct kd_pos *p, float rmin, float rmax); +void processRequests(int *listpos, int *nterm, int W); + +//compile_info.c +void output_code_options(void); + +//fit.c +void aies_mcmc(int restart); +void optimize_hybrid(int restart); +void parallel_tempering(int restart); +void print_mcmc_header(void); +void init_param(double *p); +void init_sigma(double *s); +float lnprob(double *p); +float print_chi2(void); +void get_chi2(float *chi2); +float chi2_smf(void); +float chi2_fq(void); +float chi2_csfrd(void); +float chi2_ssfr(void); +float chi2_wp(void); + +//functions.c +int compare_float(const void *a, const void *b); +int compare_id(const void *a, const void *b); +int compare_load(const void *a, const void *b); +int compare_scale(const void *a, const void *b); +int compare_totask(const void *a, const void *b); +int compare_forest(const void *a, const void *b); +int compare_size(const void *a, const void *b); +int binary_search_id(struct search_list_IDs *a, int n, IDType key); +int binary_search_float(float *a, int n, float key); +float cosmictime(float a); +float Omega(float z); +float Epeebles(float z); +float tdyn(float a); +float get_gaussian_random_number(int index); +float get_uniform_random_number(int index); +double second(void); + +//galaxies.c +void init_galaxies(void); +void finish_galaxies(void); +void make_galaxies(void); +void reset_galaxies(void); +void assemble_galaxies(void); +void assemble_tree(int itree, int thisthread); +float sfe(float hmass, float a); +float stellar_mass(int imasstab, int scalemax, int thisthread); +float intra_cluster_mass(int imass, int scalemax, int thisthread); +float get_tdf(int ihalo, int imain); + +//output.c +void output_galaxies(void); +void output_halos(void); +void output_mainbranch(void); + +//read_data.c +void read_data(void); +void read_smf(void); +void read_fq(void); +void read_csfrd(void); +void read_ssfr(void); +void read_wp(void); + +//read_trees.c +int read_trees(char *fname); +int find_files(char *fname); +void distribute_file(int ntask, int nfiles, int firstfile, int firsttask, int lasttask, int *filenr, int *master, int *last); +void share_halo_number_in_file(const char *fname, int readTask, int lastTask); +void read_tree_file(char *fname, int readTask, int lastTask); +void empty_read_buffer(int offset, int hc); +void find_forests(void); +void sort_by_size(void); +void distribute_trees(int ntask, double load); +void get_timesteps(void); +void add_orphans(void); +void setup_haloes_by_tree(void); +void setup_haloes_by_forest(void); +void compute_halo_history(void); +void find_parents(void); +void copy_trees_to_other_universes(int ntask, int nuniverses); + +//setup.c +void read_parameterfile(char *fname); +void setup(void); +void finalize (void); +void open_logfiles(void); +void close_logfiles(void); +void init_random_numbers(void); +void print_banner(void); + +//statistics.c +void add_galaxy_to_statistics(int ihalo, int thisthread); +void get_statistics(void); +void write_statistics(void); +void write_statistics_init_ascii(void); +void write_statistics_init_hdf5(void); +void write_statistics_ascii(char *outdir, int iuniverse); +void write_statistics_hdf5(char *outfname, int iuniverse); +void write_statistics_chi2(char *outdir, int step); +void write_statistics_chi2_ascii(char *outdir, int step); +void write_walker_statistics(void); +void write_walker_statistics_init_ascii(char *outfname); +void write_walker_statistics_init_hdf5(char *outfname); +void write_walker_statistics_ascii(char *outfname, int iuniverse); +void write_walker_statistics_hdf5(char *outfname, int iuniverse); + +#endif diff --git a/src/read_data.c b/src/read_data.c new file mode 100644 index 0000000..5156d6b --- /dev/null +++ b/src/read_data.c @@ -0,0 +1,628 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File read_data.c // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file read_data.c +/// \brief Contains functions that read statistical galaxy data +/// +/// This file contains all functions that read the statistical galaxy data from various files. +/// This includes the stellar mass functions, the quenched fractions, the cosmic star formation +/// rate density, and the specific star formation rates. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/*! \brief This function calls the read functions for all required data + * + * This function is called at the start of the programme and will call all read functions + * for which the READ_XYZ options have been selected. All data is read into galaxy_data structures + */ +void read_data(void) +{ + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Reading observed data...\n", All.fullline,All.startline); + +#ifdef READ_SMF + read_smf(); + All.Nobs++; +#endif + +#ifdef READ_FQ + read_fq(); + All.Nobs++; +#endif + +#ifdef READ_CSFRD + read_csfrd(); + All.Nobs++; +#endif + +#ifdef READ_SSFR + read_ssfr(); + All.Nobs++; +#endif + +#ifdef READ_WP + read_wp(); + All.Nobs++; +#endif + + MPI_Barrier(MPI_COMM_WORLD); + +} + + +#ifdef READ_SMF +/*! \brief This function reads all observed stellar mass functions from a file + * + * First the galaxy_data structure #smf is allocated. The function then reads all lines and if it is + * a header line (#) then the redshift and the corrections for IMF and Hubble_h are read. Then all + * data points in this set are read and corrected. Redshifts are stored in smf.bin. + */ +void read_smf(void) +{ + FILE *ifp; + const int linesize = 500; + char line[linesize], buf[linesize], *buffer; + int i,ismf,nset,iset; + float imf,hubble,sigmaplus,sigmaminus; + + //Task 0 reads all data + if (ThisTask == 0) + { + //Print what is done... + printf("%s Reading observed stellar mass functions.\n",All.startline); + + //Open file + if(!(ifp=fopen(All.smffile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the stellar mass functions.", All.smffile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + All.Nsmfset = 0; + All.Nsmf = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0] == '#') + { + sscanf(line,"# %d\n",&nset); + All.Nsmfset++; + All.Nsmf += nset; + } + } + //Close file + fclose(ifp); + } + + MPI_Bcast(&All.Nsmf, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.Nsmfset, 1, MPI_INT, 0, MPI_COMM_WORLD); + SmfSet = emalloc("SMFSET", All.Nsmfset * sizeof(struct data_set)); + Smf = emalloc("SMF", All.Nsmf * sizeof(struct galaxy_data)); + + //Task 0 reads all data + if (ThisTask == 0) + { + buffer = emalloc("BUFFER", SSTRING * sizeof(char)); + + //Open file + if(!(ifp=fopen(All.smffile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the stellar mass functions.", All.smffile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + ismf = iset = 0; + //Loop over all data sets + while (fgets(line,linesize,ifp)) + { + //Check if this is a new header line + if (line[0] == '#') + { + //Read the header of each set + sscanf(line,"# %d %f %f %f %f %[^\n]\n",&SmfSet[iset].ndata,&SmfSet[iset].min,&SmfSet[iset].max,&imf,&hubble,buffer); + strcpy(SmfSet[iset].tag,buffer); + //Read all data points in this set and compute sigma and correct for imf / hubble + for (i = 0; i < SmfSet[iset].ndata; i++) + { + fgets(line,linesize,ifp); + sscanf(line,"%f %f %f %f %d\n",&Smf[ismf].obs_x,&Smf[ismf].obs_y,&sigmaplus,&sigmaminus,&Smf[ismf].nfit); + Smf[ismf].obs_sigma = 0.5 * fabsf(sigmaplus - sigmaminus); + Smf[ismf].bin = 0.5 * (1.0/(SmfSet[iset].min+1.0) + 1.0/(SmfSet[iset].max+1.0)); + Smf[ismf].obs_x += -imf - 2.0 * log10(All.h_100/hubble); + Smf[ismf].obs_y += 3.0 * log10(All.h_100/hubble); + ismf++; + }//End set + iset++; + }//End header + }//End reading lines + //Close file + fclose(ifp); + efree(buffer); + + //Set the offset for each data set + if (All.Nsmfset > 0) SmfSet[0].offset = 0; + for (iset = 1; iset < All.Nsmfset; iset++) SmfSet[iset].offset = SmfSet[iset-1].offset + SmfSet[iset-1].ndata; + +#ifdef GLOBAL_SIGMA_SMF + //Add global error to SMF + for (i = 0; i < All.Nsmf; i++) + { + if (Smf[i].bin >= 1./(SIGMASMFZTHRESH+1.0)) Smf[i].obs_sigma = sqrt(Smf[i].obs_sigma*Smf[i].obs_sigma+(float)(All.GlobalSigmaSmfLz*All.GlobalSigmaSmfLz)); + else Smf[i].obs_sigma = sqrt(Smf[i].obs_sigma*Smf[i].obs_sigma+(float)(All.GlobalSigmaSmfHz*All.GlobalSigmaSmfHz)); + } +#endif + } + + //Broadcast to other tasks + MPI_Bcast(Smf, All.Nsmf * sizeof(struct galaxy_data), MPI_BYTE, 0, MPI_COMM_WORLD); + MPI_Bcast(SmfSet, All.Nsmfset * sizeof(struct data_set), MPI_BYTE, 0, MPI_COMM_WORLD); +} +#endif + + +#ifdef READ_FQ +/*! \brief This function reads all observed quenched fractions from a file + * + * First the galaxy_data structure #fq is allocated. The function then reads all lines and if it is + * a header line (#) then the redshift and the corrections for IMF and Hubble_h are read. Then all + * data points in this set are read and corrected. + */ +void read_fq(void) +{ + FILE *ifp; + const int linesize = 500; + char line[linesize], buf[linesize], *buffer; + int i,ifq,iset,nset; + float imf,hubble; + + //Task 0 reads all data + if (ThisTask == 0) + { + //Print what is done... + printf("%s Reading observed quenched fractions.\n",All.startline); + + //Open file + if(!(ifp=fopen(All.fqfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the quenched fractions.", All.fqfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + All.Nfqset = 0; + All.Nfq = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0] == '#') + { + sscanf(line,"# %d\n",&nset); + All.Nfqset++; + All.Nfq += nset; + } + } + //Close file + fclose(ifp); + } + + MPI_Bcast(&All.Nfq, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.Nfqset, 1, MPI_INT, 0, MPI_COMM_WORLD); + FqSet = emalloc("FQSET", All.Nfqset * sizeof(struct data_set)); + Fq = emalloc("FQ", All.Nfq * sizeof(struct galaxy_data)); + + //Task 0 reads all data + if (ThisTask == 0) + { + buffer = emalloc("BUFFER", SSTRING * sizeof(char)); + + //Open file + if(!(ifp=fopen(All.fqfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the quenched fractions.", All.fqfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + ifq = iset = 0; + //Loop over all data sets + while (fgets(line,linesize,ifp)) + { + //Check if this is a new header line + if (line[0] == '#') + { + //Read the header of each set + sscanf(line,"# %d %f %f %f %f %[^\n]\n",&FqSet[iset].ndata,&FqSet[iset].min,&FqSet[iset].max,&imf,&hubble,buffer); + strcpy(FqSet[iset].tag,buffer); + //Read all data points in this set and compute sigma and correct for imf / hubble + for (i = 0; i < FqSet[iset].ndata; i++) + { + fgets(line,linesize,ifp); + sscanf(line,"%f %f %f %d\n",&Fq[ifq].obs_x,&Fq[ifq].obs_y,&Fq[ifq].obs_sigma,&Fq[ifq].nfit); + Fq[ifq].bin = 0.5 * (1.0/(FqSet[iset].min+1.0) + 1.0/(FqSet[iset].max+1.0)); + Fq[ifq].obs_x += -imf - 2.0 * log10(All.h_100/hubble); + ifq++; + }//End set + iset++; + }//End header + }//End reading + //Close file + fclose(ifp); + efree(buffer); + + //Set the offset for each data set + if (All.Nfqset > 0) FqSet[0].offset = 0; + for (iset = 1; iset < All.Nfqset; iset++) FqSet[iset].offset = FqSet[iset-1].offset + FqSet[iset-1].ndata; + +#ifdef GLOBAL_SIGMA_FQ + for (i = 0; i < All.Nfq; i++) + Fq[i].obs_sigma = sqrt(Fq[i].obs_sigma*Fq[i].obs_sigma+(float)(All.GlobalSigmaFq*All.GlobalSigmaFq)); +#endif + } + + //Broadcast to other tasks + MPI_Bcast(Fq, All.Nfq * sizeof(struct galaxy_data), MPI_BYTE, 0, MPI_COMM_WORLD); + MPI_Bcast(FqSet, All.Nfqset * sizeof(struct data_set), MPI_BYTE, 0, MPI_COMM_WORLD); +} +#endif + + +#ifdef READ_CSFRD +/*! \brief This function reads all observed cosmic star formation rate densities from a file + * + * First the galaxy_data structure #csfrd is allocated. The function then reads all lines and if it is + * a header line (#) then the corrections for IMF and Hubble_h are read. Then all data points in this + * set are read and corrected. + */ +void read_csfrd(void) +{ + FILE *ifp; + const int linesize = 500; + char line[linesize], buf[linesize], *buffer; + int i,icsfrd,iset,nset; + float imf,hubble,sigmaplus,sigmaminus; + + //Task 0 reads all data + if (ThisTask == 0) + { + //Print what is done... + printf("%s Reading observed cosmic star formation rate densities.\n",All.startline); + + //Open file + if(!(ifp=fopen(All.csfrdfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the cosmic star formation rate density.", All.csfrdfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + All.Ncsfrdset = 0; + All.Ncsfrd = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0] == '#') + { + sscanf(line,"# %d\n",&nset); + All.Ncsfrdset++; + All.Ncsfrd += nset; + } + } + //Close file + fclose(ifp); + } + + MPI_Bcast(&All.Ncsfrd, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.Ncsfrdset, 1, MPI_INT, 0, MPI_COMM_WORLD); + CsfrdSet = emalloc("CSFRDSET", All.Ncsfrdset * sizeof(struct data_set)); + Csfrd = emalloc("CSFRD", All.Ncsfrd * sizeof(struct galaxy_data)); + + //Task 0 reads all data + if (ThisTask == 0) + { + buffer = emalloc("BUFFER", SSTRING * sizeof(char)); + + //Open file + if(!(ifp=fopen(All.csfrdfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the cosmic star formation rate density.", All.csfrdfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + icsfrd = iset = 0; + //Loop over all data sets + while (fgets(line,linesize,ifp)) + { + //Check if this is a new header line + if (line[0] == '#') + { + //Read the header of each set + sscanf(line,"# %d %f %f %[^\n]\n",&CsfrdSet[iset].ndata,&imf,&hubble,buffer); + strcpy(CsfrdSet[iset].tag,buffer); + //Read all data points in this set and compute sigma and correct for imf / hubble + for (i = 0; i < CsfrdSet[iset].ndata; i++) + { + fgets(line,linesize,ifp); + sscanf(line,"%f %f %f %f %d\n",&Csfrd[icsfrd].obs_x,&Csfrd[icsfrd].obs_y,&sigmaplus,&sigmaminus,&Csfrd[icsfrd].nfit); + Csfrd[icsfrd].obs_sigma = 0.5 * fabsf(sigmaplus + sigmaminus); + Csfrd[icsfrd].obs_y += -imf + log10(All.h_100/hubble/All.m_unit*All.t_unit*All.x_unit*All.x_unit*All.x_unit); + icsfrd++; + }//End set + iset++; + }//End header + }//End reading + //Close file + fclose(ifp); + efree(buffer); + + //Set the offset for each data set + if (All.Ncsfrdset > 0) CsfrdSet[0].offset = 0; + for (iset = 1; iset < All.Ncsfrdset; iset++) CsfrdSet[iset].offset = CsfrdSet[iset-1].offset + CsfrdSet[iset-1].ndata; + +#ifdef GLOBAL_SIGMA_CSFRD + for (i = 0; i < All.Ncsfrd; i++) + Csfrd[i].obs_sigma = sqrt(Csfrd[i].obs_sigma*Csfrd[i].obs_sigma+(float)(All.GlobalSigmaCsfrd*All.GlobalSigmaCsfrd)); +#endif + } + + //Broadcast to other tasks + MPI_Bcast(Csfrd, All.Ncsfrd * sizeof(struct galaxy_data), MPI_BYTE, 0, MPI_COMM_WORLD); + MPI_Bcast(CsfrdSet, All.Ncsfrdset * sizeof(struct data_set), MPI_BYTE, 0, MPI_COMM_WORLD); +} +#endif + + +#ifdef READ_SSFR +/*! \brief This function reads all observed specific star formation rates from a file + * + * First the galaxy_data structure #ssfr is allocated. The function then reads all lines and if it is + * a header line (#) then the corrections for IMF and Hubble_h are read. Then all data points in this + * set are read and corrected. Stellar masses are stored in ssfr.bin. + */ +void read_ssfr(void) +{ + FILE *ifp; + const int linesize = 500; + char line[linesize], buf[linesize], *buffer; + int i,issfr,iset,nset; + float imf,hubble,sigmaplus,sigmaminus; + + //Task 0 reads all data + if (ThisTask == 0) + { + //Print what is done... + printf("%s Reading observed specific star formation rates.\n",All.startline); + + //Open file + if(!(ifp=fopen(All.ssfrfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the cosmic star formation rate density.", All.ssfrfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + All.Nssfrset = 0; + All.Nssfr = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0] == '#') + { + sscanf(line,"# %d\n",&nset); + All.Nssfrset++; + All.Nssfr += nset; + } + } + //Close file + fclose(ifp); + } + + MPI_Bcast(&All.Nssfr, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.Nssfrset, 1, MPI_INT, 0, MPI_COMM_WORLD); + SsfrSet = emalloc("SSFRSET", All.Nssfrset * sizeof(struct data_set)); + Ssfr = emalloc("SSFR", All.Nssfr * sizeof(struct galaxy_data)); + + //Task 0 reads all data + if (ThisTask == 0) + { + buffer = emalloc("BUFFER", SSTRING * sizeof(char)); + + //Open file + if(!(ifp=fopen(All.ssfrfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the cosmic star formation rate density.", All.ssfrfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + //Initialise counter + issfr = iset = 0; + //Loop over all data sets + while (fgets(line,linesize,ifp)) + { + //Check if this is a new header line + if (line[0] == '#') + { + //Read the header of each set + sscanf(line,"# %d %f %f %[^\n]\n",&SsfrSet[iset].ndata,&imf,&hubble,buffer); + strcpy(SsfrSet[iset].tag,buffer); + //Read all data points in this set and compute sigma and correct for imf / hubble + for (i = 0; i < SsfrSet[iset].ndata; i++) + { + fgets(line,linesize,ifp); + sscanf(line,"%f %f %f %f %f %d\n",&Ssfr[issfr].obs_x,&Ssfr[issfr].bin,&Ssfr[issfr].obs_y,&sigmaplus,&sigmaminus,&Ssfr[issfr].nfit); + Ssfr[issfr].bin += -imf - 2.0 * log10(All.h_100/hubble); + Ssfr[issfr].obs_sigma = 0.5 * fabsf(sigmaplus + sigmaminus); + issfr++; + }//End set + iset++; + }//End header + }//End reading + //Close file + fclose(ifp); + efree(buffer); + + //Set the offset for each data set + if (All.Nssfrset > 0) SsfrSet[0].offset = 0; + for (iset = 1; iset < All.Nssfrset; iset++) SsfrSet[iset].offset = SsfrSet[iset-1].offset + SsfrSet[iset-1].ndata; + +#ifdef GLOBAL_SIGMA_CSFRD + for (i = 0; i < All.Nssfr; i++) + Ssfr[i].obs_sigma = sqrt(Ssfr[i].obs_sigma*Ssfr[i].obs_sigma+(float)(All.GlobalSigmaSsfr*All.GlobalSigmaSsfr)); +#endif + } + + //Broadcast to other tasks + MPI_Bcast(Ssfr, All.Nssfr * sizeof(struct galaxy_data), MPI_BYTE, 0, MPI_COMM_WORLD); + MPI_Bcast(SsfrSet, All.Nssfrset * sizeof(struct data_set), MPI_BYTE, 0, MPI_COMM_WORLD); +} +#endif + + +#ifdef READ_WP +/*! \brief This function reads all observed projected correlation functions from a file + * + * First the function reads all header lines (#) including the redshift for all correlation functions. + * Then the galaxy_data structure #Wp and the data_set structure #WpSet are allocated. The function then + * reads all lines and if it is a header line (#) then the minimum and maximum bin masses, and the + * corrections for IMF and Hubble_h are read. Then all data points in this set are read and corrected. + */ +void read_wp(void) +{ + FILE *ifp; + const int linesize = 500; + char line[linesize], buf[linesize], *buffer; + int i,iwp,iwpbin,nset; + float imf,hubble; + + //Task 0 first checks how many correlation functions there are + if (ThisTask == 0) + { + //Print what is done... + printf("%s Reading observed projected correlation functions.\n",All.startline); + + //Open file + if(!(ifp=fopen(All.wpfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the projected correlation functions.", All.wpfile_name); + endrun(buf); + } + + //Skip the header + fgets(line,linesize,ifp); + //Read the global redshift + fgets(line,linesize,ifp); + sscanf(line,"%lf\n",&All.wpredshift); + //Skip the header + fgets(line,linesize,ifp); + //Count how many different corerlation functions there are (lines starting with #) + All.Nwpset = 0; + All.Nwp = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0] == '#') + { + sscanf(line,"# %d\n",&nset); + All.Nwpset++; + All.Nwp += nset; + } + } + //Close file + fclose(ifp); + } + + MPI_Bcast(&All.Nwp, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.Nwpset, 1, MPI_INT, 0, MPI_COMM_WORLD); + WpSet = emalloc("WPBIN", All.Nwpset * sizeof(struct data_set)); + Wp = emalloc("WP", All.Nwp * sizeof(struct galaxy_data)); + + //Task 0 reads all data + if (ThisTask == 0) + { + buffer = emalloc("BUFFER", SSTRING * sizeof(char)); + + //Open file + if(!(ifp=fopen(All.wpfile_name,"r"))) + { + sprintf(buf, "Cannot open file `%s' for reading the projected correlation functions.", All.wpfile_name); + endrun(buf); + } + //Skip the header + fgets(line,linesize,ifp); + fgets(line,linesize,ifp); + fgets(line,linesize,ifp); + //Initialise counters + iwp = 0; + iwpbin = 0; + //Loop over all data sets + while (fgets(line,linesize,ifp)) + { + //Check if this is a new header line + if (line[0] == '#') + { + //Read the header of each set + sscanf(line,"# %d %f %f %f %f %f %[^\n]\n",&WpSet[iwpbin].ndata,&WpSet[iwpbin].min,&WpSet[iwpbin].max,&WpSet[iwpbin].cut,&imf,&hubble,buffer); + strcpy(WpSet[iwpbin].tag,buffer); + //Set the offset + if (iwpbin == 0) WpSet[iwpbin].offset = 0; + else WpSet[iwpbin].offset = WpSet[iwpbin-1].ndata + WpSet[iwpbin-1].offset; + //Correct the mass bins for imf/hubble + WpSet[iwpbin].min += -imf - 2.0 * log10(All.h_100/hubble); + WpSet[iwpbin].max += -imf - 2.0 * log10(All.h_100/hubble); + WpSet[iwpbin].min = pow(10.,WpSet[iwpbin].min)/All.m_unit; + WpSet[iwpbin].max = pow(10.,WpSet[iwpbin].max)/All.m_unit; + WpSet[iwpbin].cut *= hubble/All.h_100/All.x_unit; + //Read all data points in this set and compute sigma and correct for imf / hubble + for (i = 0; i < WpSet[iwpbin].ndata; i++) + { + fgets(line,linesize,ifp); + sscanf(line,"%f %f %f %d\n",&Wp[iwp].obs_x,&Wp[iwp].obs_y,&Wp[iwp].obs_sigma,&Wp[iwp].nfit); + Wp[iwp].obs_x *= hubble/All.h_100/All.x_unit; + Wp[iwp].obs_y *= hubble/All.h_100/All.x_unit; + Wp[iwp].obs_sigma *= hubble/All.h_100/All.x_unit; + iwp++; + }//End set + iwpbin++; + }//End header + }//End reading + //Close file + fclose(ifp); + efree(buffer); + +#ifdef GLOBAL_SIGMA_WP + for (i = 0; i < All.Nwp; i++) + Wp[i].obs_sigma = sqrt(Wp[i].obs_sigma*Wp[i].obs_sigma+(float)(All.GlobalSigmaWp*All.GlobalSigmaWp)*Wp[i].obs_y*Wp[i].obs_y); +#endif + //Identify the minimum and maximum mass of the correlation functions + All.wpmmin = WpSet[0].min; + All.wpmmax = WpSet[0].max; + for (i = 0; i < All.Nwpset; i++) + { + if (WpSet[i].min < All.wpmmin) All.wpmmin = WpSet[i].min; + if (WpSet[i].max > All.wpmmax) All.wpmmax = WpSet[i].max; + } + } + + //Broadcast to other tasks + MPI_Bcast(&All.wpredshift, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.wpmmin, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(&All.wpmmax, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + MPI_Bcast(Wp, All.Nwp * sizeof(struct galaxy_data), MPI_BYTE, 0, MPI_COMM_WORLD); + MPI_Bcast(WpSet, All.Nwpset * sizeof(struct data_set), MPI_BYTE, 0, MPI_COMM_WORLD); +} +#endif diff --git a/src/read_trees.c b/src/read_trees.c new file mode 100644 index 0000000..4fa8eaf --- /dev/null +++ b/src/read_trees.c @@ -0,0 +1,2789 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File read_trees.c // +// Parts of these functions have been adapted from the GADGET code developed by Volker Springel // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file read_trees.c +/// \brief Contains functions that read halo merger trees and distribute them on the processors +/// +/// This file contains all functions that are needed to read the halo merger trees and distribute +/// them to the different processors. This includes a function that identifies the forests and +/// joins all trees in a given forest on one task, a function that distributes trees or forests +/// on the tasks such that the load is balanced, a function that identifies the time steps, a +/// function that creates orphans, a function that computes halo properties based on their history, +/// a function that identifies each halo's host halo, and a function that copies the trees to the +/// tasks containing all other universes. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +#define SKIP fread(&blksize,sizeof(int),1,ifp); ///< Reads in a block with the size of an integer + + +/*! \brief This function reads all merger trees and distributes them on the tasks + * + * This function reads all merger trees from several files and distributes them smoothly + * on NTaskPerUniverse tasks. Then the routines for setting up the trees are called. + * + * \param fname Filename to be read + */ +int read_trees(char *fname) +{ + int i, rep, num_files, rest_files, ngroups, master, filenr, masterTask, lastTask, max_load; + int *nhalosInTask, *ntreesInTask; + char buf[NSTRING]; + unsigned long long NtreesLocal, NhalosLocal; + + All.TotNhalos = 0; + All.TotNtrees = 0; + + //Get number of files + num_files = find_files(fname); + + //Repeat reading the headers of the files twice. In the first iteration, only the tree and halo numbers ending + //up on each task are assembled, followed by memory allocation. In the second iteration, the data is actually read in. + for (rep = 0; rep < 2; rep++) + { + + Nhalos = 0; + Ntrees = 0; + + //Set the files left to read equal to the number of files + rest_files = num_files; + + //First do all files one per task + while(rest_files > All.NTaskPerUniverse) + { + //Set the file name (here there are always multiple files) + sprintf(buf, "%s.%d", fname, ThisTask + (rest_files - All.NTaskPerUniverse)); + + //Number of groups and master task + ngroups = All.NTaskPerUniverse / All.NumFilesInParallel; + if((All.NTaskPerUniverse % All.NumFilesInParallel)) ngroups++; + master = (ThisTask / ngroups) * ngroups; + + //Loop through all tasks in the first universe + for(i = 0; i < ngroups; i++) + { + if ((ThisTask == (master + i)) && (ThisTask < All.NTaskPerUniverse)) + { + //This processor's turn + if (rep == 0) share_halo_number_in_file(buf, ThisTask, ThisTask); + else read_tree_file(buf, ThisTask, ThisTask); + } + MPI_Barrier(MPI_COMM_WORLD); + } + + //Now the number of files is reduced by the number files just read + rest_files -= All.NTaskPerUniverse; + + } + + //Then do remaining files and distribute to tasks + if(rest_files > 0) + { + //Since there are fewer files than read tasks we need to ditribute the tasks + distribute_file(All.NTaskPerUniverse, rest_files, 0, 0, All.NTaskPerUniverse-1, &filenr, &masterTask, &lastTask); + + //Get file name either for one or multiple files + if(num_files > 1) sprintf(buf, "%s.%d", fname, filenr); + else sprintf(buf, "%s", fname); + + //Number of groups + ngroups = rest_files / All.NumFilesInParallel; + if((rest_files % All.NumFilesInParallel)) ngroups++; + + //Loop through all files left + for(i = 0; i < ngroups; i++) + { + if ((filenr / All.NumFilesInParallel == i) && (ThisTask < All.NTaskPerUniverse)) + { + //This processor's turn + if (rep == 0) share_halo_number_in_file(buf, masterTask, lastTask); + else read_tree_file(buf, masterTask, lastTask); + } + MPI_Barrier(MPI_COMM_WORLD); + } + } + + //Determine total number of trees and haloes and allocate memory + if (rep == 0) + { + //Calculate total number of trees and haloes + NtreesLocal = (unsigned long long)(Ntrees); + NhalosLocal = (unsigned long long)(Nhalos); + MPI_Allreduce(&NtreesLocal, &All.TotNtrees, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&NhalosLocal, &All.TotNhalos, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + + //Determine the maximum number of haloes per task including some tolerance + MPI_Allreduce(&Nhalos, &max_load, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + All.MaxHalos = max_load / (1.0 - 2 * ALLOC_TOLERANCE); + + //Determine the maximum number of trees per task including some tolerance + MPI_Allreduce(&Ntrees, &max_load, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + All.MaxTrees = max_load / (1.0 - 2 * ALLOC_TOLERANCE) * 2; + + //Allocate the number of haloes in each tree and the haloes + NhalosInTree = (int *) emalloc_movable(&NhalosInTree, "NhalosInTree", All.MaxTrees * sizeof(int)); + OffsetHalos = (int *) emalloc_movable(&OffsetHalos, "OffsetHalos", All.MaxTrees * sizeof(int)); +#ifdef COMPUTE_ICM + NtreesInForest = (int *) emalloc_movable(&NtreesInForest, "NtreesInForest", All.MaxTrees * sizeof(int)); + NhalosInForest = (int *) emalloc_movable(&NhalosInForest, "NhalosInForest", All.MaxTrees * sizeof(int)); +#endif + H = (struct halo *) emalloc_movable(&H, "Halos", All.MaxHalos * sizeof(struct halo)); + TreeIDInTree = (IDType *) emalloc_movable(&TreeIDInTree, "TreeIDInTree", All.MaxTrees * sizeof(IDType)); + + //Set all to zero + memset(NhalosInTree, 0, All.MaxTrees * sizeof(int)); + memset(OffsetHalos, 0, All.MaxTrees * sizeof(int)); +#ifdef COMPUTE_ICM + memset(NtreesInForest, 0, All.MaxTrees * sizeof(int)); + memset(NhalosInForest, 0, All.MaxTrees * sizeof(int)); +#endif + memset(H, 0, All.MaxHalos * sizeof(struct halo)); + memset(TreeIDInTree, 0, All.MaxTrees * sizeof(IDType)); + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Reading %llu trees with %llu haloes on %d tasks...\n", All.fullline,All.startline,All.TotNtrees,All.TotNhalos,All.NumFilesInParallel); + + //Allocate the CommBuffer + CommBuffer = emalloc_movable(&CommBuffer,"CommBuffer", All.BufferSize * 1024 * 1024); + + MPI_Barrier(MPI_COMM_WORLD); + } + + if (rep == 1) efree_movable(CommBuffer); + + } + + //If we want to compute the ICM we need to collect all trees in forests +#ifdef COMPUTE_ICM + find_forests(); +#endif + + //Now we can free TreeIDInTree; + efree_movable(TreeIDInTree); + + //Sort the trees or forests by their size, so that smaller trees/forests can be moved to other tasks + sort_by_size(); + + //Since the number of haloes is not yet the same on all tasks they are distributed more evenly + distribute_trees(All.NTaskPerUniverse, (double)(Nhalos)); + + //Report memory usage + report_memory_usage(&HighMark, "Trees_Read"); + + //Identify the scale factors and the number of timesteps + get_timesteps(); + + //Add orphans to the trees - each leaf continues to the last timestep + add_orphans(); + + //Determine total number of trees and haloes + NtreesLocal = (unsigned long long)(Ntrees); + NhalosLocal = (unsigned long long)(Nhalos); + MPI_Allreduce(&NtreesLocal, &All.TotNtrees, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&NhalosLocal, &All.TotNhalos, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + + //Sort the trees or forests by their size, so that smaller trees/forests can be moved to other tasks + sort_by_size(); + + //Since we now have added orphans we need to re-distribute the trees more evenly again + distribute_trees(All.NTaskPerUniverse, (double)(Nhalos)); + +#ifndef COMPUTE_ICM + //Setup all haloes (progenitor/descendant connection) tree-by-tree + setup_haloes_by_tree(); +#else + //Setup all haloes (progenitor/descendant connection) forest-by-forest + setup_haloes_by_forest(); + //Find the parent of each halo in each forest + find_parents(); +#endif + + //Compute peak mass and accretion rate + compute_halo_history(); + + //For communication purposed + ntreesInTask = emalloc("NtreesInTask", NTask * sizeof(int)); + nhalosInTask = emalloc("NhalosInTask", NTask * sizeof(int)); + + //Gather the number of trees and haloes locally + MPI_Gather(&Ntrees, 1, MPI_INT, ntreesInTask, 1, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Gather(&Nhalos, 1, MPI_INT, nhalosInTask, 1, MPI_INT, 0, MPI_COMM_WORLD); + + //And print + if (ThisTask == 0) + { + printf("%s\n",All.fullline); + for (i=0; i 1) copy_trees_to_other_universes(All.NTaskPerUniverse,All.NUniverses); + + return 1; + +} + + +/*! \brief This function determines on how many files the halo merger trees are distributed. + * + * The root task first checks if there is a file that has a file name equal to fname. In this + * case a number of files equal to 1 is returned. Otherwise it checks if there are files that + * have a file name equal to fname.number and counts how many files there are. This number is + * then returned. + * + * \param fname Filename to be read + */ +int find_files(char *fname) +{ + FILE *fd; + char buf[NSTRING]; + int num_files, loop; + + num_files = 0; + loop = 1; + sprintf(buf, "%s", fname); + + if(ThisTask == 0) + { + if((fd = fopen(buf, "r"))) { + fclose(fd); + num_files = 1; + } + else + { + while (loop) + { + sprintf(buf, "%s.%d", fname,num_files); + if((fd = fopen(buf, "r"))) num_files++; + else loop = 0; + if (loop > 0) fclose(fd); + } + } + } + + MPI_Bcast(&num_files, 1, MPI_INT, 0, MPI_COMM_WORLD); + + return num_files; + +} + + +/*! \brief This function assigns a certain number of tasks to each file. + * + * These tasks are containing the content of that file after the trees have been read + * The number of tasks per file is as homogeneous as possible. + * The number of files may at most be equal to the number of tasks. + * + * \param ntask Number of tasks on which the files will be distributed + * \param nfiles Number of files on which the trees are located + * \param firstfile Index of the first file + * \param firsttask Index of the first task + * \param lasttask Index of the last task + * \param filenr Contains the file number to which this task belongs + * \param master The number of the task responsible to read the file + * \param last Number of the last task belonging to the same file as this task + */ +void distribute_file(int ntask, int nfiles, int firstfile, int firsttask, int lasttask, int *filenr, int *master, int *last) +{ + int i, group; + int tasks_per_file = ntask / nfiles; + int tasks_left = ntask % nfiles; + + if(tasks_left == 0) + { + group = ThisTask / tasks_per_file; + *master = group * tasks_per_file; + *last = (group + 1) * tasks_per_file - 1; + *filenr = group; + return; + } + + for(i = 0, *last = -1; i < nfiles; i++) + { + *master = *last + 1; + *last = (i + 1) * ((double) ntask) / nfiles; + if(*last >= ntask) *last = *last - 1; + if(*last < *master) endrun("last < master"); + *filenr = i; + if(i == nfiles - 1) *last = ntask - 1; + if(ThisTask >= *master && ThisTask <= *last) return; + } +} + + +/*! \brief This function reads the tree and halo numbers in the file fname and distributes them + * to tasks 'readTask' to 'lastTask'. + * + * First the number of trees in each file is read and communicated to all tasks. Then the array + * containing the number of haloes for each tree is read and communicated. The trees are divided + * among tasks such that the number of haloes for each task is as close as possible. This information + * is stored in Ntrees and Nhalos for each task. + * + * \param fname Filename to be read + * \param readTask Task responsible for reading the file fname + * \param lastTask Last Task which gets data contained in the file + */ +void share_halo_number_in_file(const char *fname, int readTask, int lastTask) +{ + int i, itree, ntask, task, n_in_task; + int ntrees_in_file, totNhalos_in_file; + int *nhalos_in_file; + int blksize; + MPI_Status status; + FILE *ifp; + char buf[500]; + + //First let the readTask read the number of trees in this file and communicate + if(ThisTask == readTask) + { + //Open the file + if(!(ifp = fopen(fname, "r"))) + { + sprintf(buf, "Can't open file `%s' for reading merger trees.", fname); + endrun(buf); + } + + //Read the number of trees in this file + SKIP; + fread(&ntrees_in_file, sizeof(int), 1, ifp); + SKIP; + + //Check if this was an integer + if(blksize != 4) + endrun("incorrect file format\n"); + + //Send this to all other tasks in the group + for(task = readTask + 1; task <= lastTask; task++) + { + MPI_Ssend(&ntrees_in_file, 1, MPI_INT, task, TAG_NTREES, MPI_COMM_WORLD); + } + } + //All other tasks receive the number of trees in this file here + else + { + MPI_Recv(&ntrees_in_file, 1, MPI_INT, readTask, TAG_NTREES, MPI_COMM_WORLD, &status); + } + + //Allocate nhalos_in_file + if(!(nhalos_in_file = (int *) emalloc("Nhalos_in_file",ntrees_in_file*sizeof(int)))) + endrun("Failed to allocate memory for nhalos_in_file."); + + //Now read the nhalos_in_file array + if(ThisTask == readTask) + { + //Read the array containing the number of haloes for each tree + SKIP; + fread(nhalos_in_file, sizeof(int), ntrees_in_file, ifp); + SKIP; + + //Send this to all other tasks in the group + for(task = readTask + 1; task <= lastTask; task++) + { + MPI_Ssend(nhalos_in_file, ntrees_in_file, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD); + } + } + //All other tasks receive nhalos_in_file here + else + { + MPI_Recv(nhalos_in_file, ntrees_in_file, MPI_INT, readTask, TAG_NHALOS, MPI_COMM_WORLD, &status); + } + + //Get the total number of tasks in this group + ntask = lastTask - readTask + 1; + + //Count total number of haloes in this file + totNhalos_in_file = 0; + for (i = 0; i < ntrees_in_file; i++) totNhalos_in_file += nhalos_in_file[i]; + + //Initialise counters + itree = n_in_task = 0; + task = readTask; + //Loop through all halos in file + for (i = 0; i < ntrees_in_file; i++) + { + //Add number of haloes in this tree to current number + n_in_task += nhalos_in_file[i]; + itree++; + //If current number of haloes is larger than the equal fraction + if (n_in_task > totNhalos_in_file / ntask) + { + //Add number of trees and haloes to this task + if (ThisTask == task) + { + Ntrees += itree; + Nhalos += n_in_task; + } + //Now do next task and reset counters + task++; + itree = 0; + n_in_task = 0; + } + //Go to next tree + } + //Since the last task has fewer haloes than the equal fraction, do it seperately + if (ThisTask == lastTask) + { + Ntrees += itree; + Nhalos += n_in_task; + } + + efree(nhalos_in_file); + + //Close the file again + if(ThisTask == readTask) fclose(ifp); + +} + + +/*! \brief This function reads the halos in the file fname and distributes them to tasks 'readTask' to 'lastTask'. + * + * First the number of trees in each file is read and communicated to all tasks. Then the array + * containing the number of haloes for each tree is read and communicated. The trees are divided + * among tasks such that the number of haloes for each task is as close as possible. This information + * is stored in Ntrees and Nhalos for each task. + * + * \param fname Filename to be read + * \param readTask Task responsible for reading the file fname + * \param lastTask Last Task which gets data contained in the file + */ +void read_tree_file(char *fname, int readTask, int lastTask) +{ + int i, itree, hc, ntask, task, n_in_task, offset; + int ntrees_in_file, totNhalos_in_file; + int *nhalos_in_file, *nhalo_for_this_task, *ntree_for_this_task; + int blksize, maxlen; + IDType *treeID_in_file; + MPI_Status status; + FILE *ifp; + char buf[500]; + + //First let the readTask read the number of trees in this file and communicate + if(ThisTask == readTask) + { + //Open the file + if(!(ifp = fopen(fname, "r"))) + { + sprintf(buf, "Can't open file `%s' for reading merger trees.", fname); + endrun(buf); + } + + //Read the number of trees in this file + SKIP; + fread(&ntrees_in_file, sizeof(int), 1, ifp); + SKIP; + + //Check if this was an integer + if(blksize != 4) + endrun("incorrect file format\n"); + + //Send this to all other tasks in the group + for(task = readTask + 1; task <= lastTask; task++) + { + MPI_Ssend(&ntrees_in_file, 1, MPI_INT, task, TAG_NTREES, MPI_COMM_WORLD); + } + } + //All other tasks receive the number of trees in this file here + else + { + MPI_Recv(&ntrees_in_file, 1, MPI_INT, readTask, TAG_NTREES, MPI_COMM_WORLD, &status); + } + + //Allocate nhalos_in_file and treeID_in_file + nhalos_in_file = emalloc("Nhalos_in_file",ntrees_in_file*sizeof(int)); + treeID_in_file = emalloc("TreeID_in_file",ntrees_in_file*sizeof(IDType)); + + //Now read the nhalos_in_file array + if(ThisTask == readTask) + { + //Read the array containing the number of haloes for each tree + SKIP; + fread(nhalos_in_file, sizeof(int), ntrees_in_file, ifp); + SKIP; + + SKIP; + fread(treeID_in_file, sizeof(IDType), ntrees_in_file, ifp); + SKIP; + + //Send this to all other tasks in the group + for(task = readTask + 1; task <= lastTask; task++) + { + MPI_Ssend(nhalos_in_file, ntrees_in_file, MPI_INT, task, TAG_NHALOS, MPI_COMM_WORLD); + MPI_Ssend(treeID_in_file, ntrees_in_file * sizeof(IDType), MPI_BYTE, task, TAG_TREEID, MPI_COMM_WORLD); + } + } + //All other tasks receive nhalos_in_file here + else + { + MPI_Recv(nhalos_in_file, ntrees_in_file, MPI_INT, readTask, TAG_NHALOS, MPI_COMM_WORLD, &status); + MPI_Recv(treeID_in_file, ntrees_in_file * sizeof(IDType), MPI_BYTE, readTask, TAG_TREEID, MPI_COMM_WORLD, &status); + } + + //Get the total number of tasks in this group + ntask = lastTask - readTask + 1; + + //Allocate ntree_for_this_task + ntree_for_this_task = emalloc("ntree_for_this_task",ntask*sizeof(int)); + + //Allocate nhalo_for_this_task + nhalo_for_this_task = (int *) emalloc("nhalo_for_this_task",ntask*sizeof(int)); + + //Count total number of haloes in this file + totNhalos_in_file = 0; + for (i = 0; i < ntrees_in_file; i++) totNhalos_in_file += nhalos_in_file[i]; + + //Initialise counters + task = itree = n_in_task = 0; + //Loop through all halos in file + for (i = 0; i < ntrees_in_file; i++) + { + //Add number of haloes in this tree to current number + n_in_task += nhalos_in_file[i]; + itree++; + //If current number of haloes is larger than the equal fraction + if (n_in_task > totNhalos_in_file / ntask) + { + //Add number of trees and haloes to task + nhalo_for_this_task[task] = n_in_task; + ntree_for_this_task[task] = itree; + //Now do next task and reset counters + task++; + itree = 0; + n_in_task = 0; + } + //Go to next tree + } + //Since the last task has fewer haloes than the equal fraction, do it seperately + nhalo_for_this_task[ntask-1] = n_in_task; + ntree_for_this_task[ntask-1] = itree; + + //Print the file details + if(ThisTask == readTask) + { + printf("%s Reading file `%s' on task=%d (contains %d trees and %d haloes).\n" + "%s Distributing this file to tasks %d-%d\n", + All.startline, fname, ThisTask, ntrees_in_file, totNhalos_in_file, All.startline, readTask, lastTask); + fflush(stdout); + } + + //Move NhalosInTree and H by ntree_for_this_task so there is exactly the right amount of space to hold the new data + memmove(&NhalosInTree[ntree_for_this_task[ThisTask-readTask]], &NhalosInTree[0], Ntrees * sizeof(int)); + memmove(&TreeIDInTree[ntree_for_this_task[ThisTask-readTask]], &TreeIDInTree[0], Ntrees * sizeof(IDType)); + memmove(&H[nhalo_for_this_task[ThisTask-readTask]], &H[0], Nhalos * sizeof(struct halo)); + + //Get the maximum number of haloes the CommBuffer can hold + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / (sizeof(struct haloread)); + itree = 0; + offset = 0; + + //Start reading the haloes + if(ThisTask == readTask) SKIP; + + //Loop over tasks in this group + for(task = readTask; task <= lastTask; task++) + { + //Check if this task has enough memory to store the new haloes + if(task == ThisTask) + if(Nhalos + nhalo_for_this_task[task-readTask] > All.MaxHalos) + { + sprintf(buf, "too many haloes. %d %d %d\n", Nhalos, nhalo_for_this_task[task-readTask], All.MaxHalos); + endrun(buf); + } + + //Copy the number of haloes for each tree for each task + for (i=0; i maxlen) hc = maxlen; + + //If it is the reading task read hc haloes from the file and write it to the buffer + if(ThisTask == readTask) fread(CommBuffer, sizeof(struct haloread), hc, ifp); + + //If it is the reading task and there are haloes to read send the CommBuffer to other task + if(ThisTask == readTask && task != readTask && hc > 0) + MPI_Ssend(CommBuffer, (sizeof(struct haloread)) * hc, MPI_BYTE, task, TAG_HDATA, MPI_COMM_WORLD); + + //If it is not the reading task and there are haloes to read receive the CommBuffer from the reading task + if(ThisTask != readTask && task == ThisTask && hc > 0) + MPI_Recv(CommBuffer, (sizeof(struct haloread)) * hc, MPI_BYTE, readTask, TAG_HDATA, MPI_COMM_WORLD, &status); + + //If this is the task where the haloes should end + if(ThisTask == task) + { + //Read out the haloes from the CommBuffer and increment the offset by the number of haloes just read + empty_read_buffer(offset, hc); + offset += hc; + } + + //Remove the number of haloes just read from the number of haloes left to read + nhalo_for_this_task[task-readTask] -= hc; + } + //Loop until all haloes are read + while(nhalo_for_this_task[task-readTask] > 0); + } + + //When every halo has been read finish reading the file + if(ThisTask == readTask) SKIP; + + //Initialise counters + itree = n_in_task = 0; + task = readTask; + //Loop through all halos in file + for (i = 0; i < ntrees_in_file; i++) + { + //Add number of haloes in this tree to current number + n_in_task += nhalos_in_file[i]; + itree++; + //If current number of haloes is larger than the equal fraction + if (n_in_task > totNhalos_in_file / ntask) + { + //Add number of trees and haloes to this task + if (ThisTask == task) + { + Ntrees += itree; + Nhalos += n_in_task; + } + //Now do next task and reset counters + task++; + itree = 0; + n_in_task = 0; + } + //Go to next tree + } + //Since the last task has fewer haloes than the equal fraction, do it seperately + if (ThisTask == lastTask) + { + Ntrees += itree; + Nhalos += n_in_task; + } + + efree(nhalo_for_this_task); + efree(ntree_for_this_task); + efree(treeID_in_file); + efree(nhalos_in_file); + + //Close the file again + if(ThisTask == readTask) fclose(ifp); + +} + + +/*! \brief This function reads out the CommBuffer that was filled with merger tree data. + * + * The CommBuffer is cast into the halo structure and hc haloes are read from it. + * Masses are converted from Msol/h to code units (typically 1e9Msol). + * Positions are converted from Mpc/h to code units (typically Mpc). + * Radii are converted from Mpc/h to 1/1000 code units (typically kpc). + * + * \param offset Halo corresponding to the first element in the CommBuffer + * \param hc Number of haloes in the CommBuffer + */ +void empty_read_buffer(int offset, int hc) +{ + int n; + struct haloread *h; + + //Cast CommBuffer into halo structure + h = (struct haloread *) CommBuffer; + + //Copy values for each halo to H +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for schedule(static,10) +#endif + for(n = 0; n < hc; n++) + { //In case the positions are out of bounds put all haloes back into the box + if (h[n].pos[0] >= (float)(All.Lbox)) h[n].pos[0] = fmod(h[n].pos[0], (float)(All.Lbox)); + if (h[n].pos[1] >= (float)(All.Lbox)) h[n].pos[1] = fmod(h[n].pos[1], (float)(All.Lbox)); + if (h[n].pos[2] >= (float)(All.Lbox)) h[n].pos[2] = fmod(h[n].pos[2], (float)(All.Lbox)); + if (h[n].pos[0] < 0) h[n].pos[0] = fmod(h[n].pos[0], (float)(All.Lbox)) + (float)(All.Lbox); + if (h[n].pos[1] < 0) h[n].pos[1] = fmod(h[n].pos[1], (float)(All.Lbox)) + (float)(All.Lbox); + if (h[n].pos[2] < 0) h[n].pos[2] = fmod(h[n].pos[2], (float)(All.Lbox)) + (float)(All.Lbox); + + H[offset + n].haloid = h[n].haloid; + H[offset + n].descid = h[n].descid; + H[offset + n].upid = h[n].upid; + + H[offset + n].np = h[n].np; + H[offset + n].mmp = h[n].mmp; + + H[offset + n].a = h[n].a; + H[offset + n].mvir = h[n].mvir/All.h_100/All.m_unit;; + H[offset + n].rvir = h[n].rvir/All.h_100/All.x_unit/1000.0; + H[offset + n].c = h[n].c; + H[offset + n].lambda = h[n].lambda; + + H[offset + n].pos[0] = h[n].pos[0]/All.h_100/All.x_unit;; + H[offset + n].pos[1] = h[n].pos[1]/All.h_100/All.x_unit;; + H[offset + n].pos[2] = h[n].pos[2]/All.h_100/All.x_unit;; + H[offset + n].vel[0] = h[n].vel[0]; + H[offset + n].vel[1] = h[n].vel[1]; + H[offset + n].vel[2] = h[n].vel[2]; + + H[offset + n].idesc = -1; + H[offset + n].iprog = -1; + H[offset + n].icoprog = -1; + } + +} + + +#ifdef COMPUTE_ICM +/*! \brief This function identifies all forests and collects them on one task. + * + * First the forest file is read and the forest IDs belonging to each tree and halo are stored. + * Then the function checks which halo needs to go to which task to collect all trees/haloes in + * a forest and stores this info (temporarily in imain). The halo array is then sorted according + * to where the haloes need to go. Going through all task pairs the trees and haloes are exchanged. + * Finally the halo array is sorted according to the forest ID (and then according to the tree ID), + * and the number of trees and haloes in each forest is computed. + */ +void find_forests(void) +{ + int i, j, read, iforest, itree, ihalo, isend, irecv, maxlen, send_this_turn, left_to_send; + int nids, ntreesInTask, nhalosInTask, ntrees_send, nhalos_send; + int *ntreesToTask, *nhalosToTask, *ntreesOffset, *nhalosOffset, *ip; + unsigned long long NtreesLocal, NhalosLocal, trees_to_read; + IDType *forestID, *treeID, *forestIDTree; + + struct search_list_IDs *id_list; + struct halo *hp; + MPI_Status status; + + FILE *ifp; + const int linesize = 1000; + char line[linesize], buf[500], forestfilename[NSTRING]; + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Identifying the forests...\n", All.fullline,All.startline); + + //Allocate arrays + nids = ((int) (All.BufferSize * 1024 * 1024 / 10)) / (sizeof(IDType)); + id_list = emalloc_movable(&id_list, "id_list", All.MaxTrees * sizeof(struct search_list_IDs)); + forestIDTree = emalloc_movable(&forestIDTree, "ForestIDTree", All.MaxTrees * sizeof(IDType)); + forestID = emalloc("ForestID", nids * sizeof(IDType)); + treeID = emalloc("TreeID", nids * sizeof(IDType)); + + //Write all tree IDs in this task in id_list +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for +#endif + for (i = 0; i < Ntrees; i++) + { + id_list[i].ID = TreeIDInTree[i]; + id_list[i].position = i; + } + + //Sort all tree IDs on this task + qsort(id_list, Ntrees, sizeof(struct search_list_IDs), compare_id); + + //Get the forest file name + sprintf(forestfilename, "%s.forests", All.treefile_name); + + if (ThisTask==0) + { + //Open the file on task 0 + if(!(ifp = fopen(forestfilename, "r"))) + { + sprintf(buf, "Can't open file `%s' for reading the forests.", forestfilename); + endrun(buf); + } + } + + //Read the forest file + i = 0; + read = 1; + trees_to_read = All.TotNtrees; + + if (ThisTask==0) fgets(line,linesize,ifp); //1st line is a header + + //Go through each line + do + { + //Only task 0 reads + if (ThisTask==0) + { + //Read line + if (fgets(line,linesize,ifp)) + { + //Write to treeID and forestID +#ifndef LONGIDS + sscanf(line,"%u %u\n",&treeID[i],&forestID[i]); +#else + sscanf(line,"%llu %llu\n",&treeID[i],&forestID[i]); +#endif + read = 1; + } + else read = 0; + } + + //Increment the index + if (ThisTask == 0) + { + //If this is the reading task (0) and we still read increment i by 1 + if (read == 1) i++; + } + else + { + //If this is not the reading task and we have more trees to read than one set increment by nids + if (trees_to_read >= (unsigned long long)(nids)) i += nids; + //Otherwise increment by the number of trees left to read and set read to 0 + else + { + i += (int)(trees_to_read); + read = 0; + } + } + + //If one set has been read link the ids + if (i == nids) + { + //If we still need to read then tell this to the other tasks + MPI_Bcast(&read, 1, MPI_INT, 0, MPI_COMM_WORLD); + + //Send this set to all other tasks +#ifndef LONGIDS + MPI_Bcast(treeID, nids, MPI_UNSIGNED, 0, MPI_COMM_WORLD); + MPI_Bcast(forestID, nids, MPI_UNSIGNED, 0, MPI_COMM_WORLD); +#else + MPI_Bcast(treeID, nids, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(forestID, nids, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); +#endif + //Each task searches for each tree ID in the set and if found sets its forest ID + for (j = 0; j < nids; j++) + { + itree = binary_search_id(id_list, Ntrees, treeID[j]); + if (itree >= 0) forestIDTree[itree] = forestID[j]; + } + //Reset the index so we can fill the buffer again + i = 0; + //Decrease the number of trees left to read by nids + trees_to_read -= (unsigned long long)(nids); + } + //Continute reading + } while (read); + + //set the remaining IDs if there are some left + if (i > 0) + { + //Send this set to all other tasks +#ifndef LONGIDS + MPI_Bcast(treeID, i, MPI_UNSIGNED, 0, MPI_COMM_WORLD); + MPI_Bcast(forestID, i, MPI_UNSIGNED, 0, MPI_COMM_WORLD); +#else + MPI_Bcast(treeID, i, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); + MPI_Bcast(forestID, i, MPI_UNSIGNED_LONG_LONG, 0, MPI_COMM_WORLD); +#endif + + //Each task searches for each tree ID in the set and if found sets its forestID + for (j = 0; j < i; j++) + { + itree = binary_search_id(id_list, Ntrees, treeID[j]); + if (itree >= 0) forestIDTree[itree] = forestID[j]; + } + } + + //Free the read arrays + efree(treeID); + efree(forestID); + + //Set the forestID of each halo + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + H[ihalo].forestid = forestIDTree[itree]; + + //Now join all forests by moving them from task to task + + //In order to sort the haloes easily we misuse iscale as original index and idesc as number of haloes in tree + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + { + H[ihalo].iscale = ihalo; + H[ihalo].idesc = NhalosInTree[itree]; + } + + //Allocate the arrays that store how many trees/haloes to move to other tasks and where they are + ntreesToTask = emalloc_movable(&ntreesToTask,"NtreesToTask", NTask * sizeof(int)); + nhalosToTask = emalloc_movable(&nhalosToTask,"NhalosToTask", NTask * sizeof(int)); + ntreesOffset = emalloc_movable(&ntreesOffset,"NtreesOffset", NTask * sizeof(int)); + nhalosOffset = emalloc_movable(&nhalosOffset,"NhalosOffset", NTask * sizeof(int)); + //Initialise as zero + for (i = 0; i < NTask; i++) + { + ntreesToTask[i] = 0; + nhalosToTask[i] = 0; + } + + //Allocate forestID array and set it to zero + forestID = emalloc_movable(&forestID,"ForestID", All.MaxTrees * sizeof(IDType)); + memset(forestID,0,All.MaxTrees * sizeof(IDType)); + //treeID = emalloc("TreeID", All.MaxTrees * sizeof(IDType)); + + //Loop over all tasks that have to send trees + for (isend = 0; isend < All.NTaskPerUniverse; isend++) + { + if (ThisTask == isend) + { + //Copy the forest IDs to forestID and set the number of haloes in this task + for (i = 0; i < Ntrees; i++) forestID[i] = forestIDTree[i]; + ntreesInTask = Ntrees; + } + //Send the number of haloes in the send task to all tasks + MPI_Bcast(&ntreesInTask, 1, MPI_INT, isend, MPI_COMM_WORLD); + //Send the forestID array to all other tasks +#ifndef LONGIDS + MPI_Bcast(forestID, ntreesInTask, MPI_UNSIGNED, isend, MPI_COMM_WORLD); +#else + MPI_Bcast(forestID, ntreesInTask, MPI_UNSIGNED_LONG_LONG, isend, MPI_COMM_WORLD); +#endif + + //Go through all other tasks that can receive trees + for (irecv = 0; irecv < All.NTaskPerUniverse; irecv++) + { + //Go through all haloes/trees on this task + for (itree = 0, ihalo = 0; itree < ntreesInTask; itree++, ihalo+=H[ihalo].idesc) + { + //If this is the receiving task search the forest ID in the tree ID list + if (ThisTask == irecv) iforest = binary_search_id(id_list, Ntrees, forestID[itree]); + //Send this index to all other tasks + MPI_Bcast(&iforest, 1, MPI_INT, irecv, MPI_COMM_WORLD); + //If it has been found and this is the sending task... + if (iforest >= 0 && ThisTask == isend) + { + //... increment the number of tree going to the receiving task + ntreesToTask[irecv]++; + //imain now stores the task where the tree needs to go + for (j = ihalo; j < ihalo + H[ihalo].idesc; j++) H[j].imain = irecv; + } + } + } + } + + //Print what is done... + if (ThisTask == 0) printf("%s Sorting haloes by the task to which they will be moved...\n",All.startline); + + //Sort all haloes according to which task they need to be send + qsort(H, Nhalos, sizeof(struct halo), compare_totask); + + //As the sorting destroyed the old one - get the new NhalosInTrees array + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + NhalosInTree[itree] = H[ihalo].idesc; + ihalo += NhalosInTree[itree]; + } + + //Count how many haloes go to each task + for (i = 0; i < Nhalos; i++) nhalosToTask[H[i].imain]++; + + //Compute the offsets that is the starting indices of each set of trees/haloes + ntreesOffset[0] = 0; + nhalosOffset[0] = 0; + for (i = 1; i < NTask; i++) + { + ntreesOffset[i] = ntreesOffset[i-1] + ntreesToTask[i-1]; + nhalosOffset[i] = nhalosOffset[i-1] + nhalosToTask[i-1]; + } + + //Compute how many haloes are sent to other tasks (for printing) + ntrees_send = 0; + nhalos_send = 0; + for (i = 0; i < All.NTaskPerUniverse; i++) + { + if (i != ThisTask) + { + ntrees_send += ntreesToTask[i]; + nhalos_send += nhalosToTask[i]; + } + } + + //Print what is done... + if (MasterTask == 0) + printf("%s Task %6d is moving %6d trees with %8d haloes to other tasks.\n",All.startline,ThisTask,ntrees_send,nhalos_send); + + //Allocate the CommBuffer + CommBuffer = emalloc_movable(&CommBuffer,"CommBuffer", All.BufferSize * 1024 * 1024); + + //Go through all pairs and exchange the trees and haloes + for (isend = 1; isend < All.NTaskPerUniverse; isend++) + { + //irecv is always at least 1 smaller than isend + for (irecv = 0; irecv < isend; irecv++) + { + //First send from isend to irecv + + //Get number of trees that will be sent and broadcast + ntrees_send = ntreesToTask[irecv]; + MPI_Bcast(&ntrees_send, 1, MPI_INT, isend, MPI_COMM_WORLD); + //Get number of trees that are already at the irecv task and broadcast + ntreesInTask = Ntrees; + MPI_Bcast(&ntreesInTask, 1, MPI_INT, irecv, MPI_COMM_WORLD); + + //Check if the receiving task has enough space for the new trees + if(ntreesInTask + ntrees_send > All.MaxTrees) + { + //If we need more trees than currently allocated then reallocate + All.MaxTrees = ntreesInTask + ntrees_send; + NhalosInTree = (int *) erealloc_movable(NhalosInTree, All.MaxTrees * sizeof(int)); + } + + //The isend task sends all NhalosInTree to the irecv task + if (ThisTask == isend && ntrees_send > 0) + { + ip = (int *) CommBuffer; + //Write CommBuffer from NhalosInTree + for (i = ntreesOffset[irecv]; i < ntreesOffset[irecv]+ntrees_send; i++) + *ip++ = NhalosInTree[i]; + //If there are trees to send then send the CommBuffer to the receiving task + MPI_Ssend(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, irecv, TAG_NHALOS, MPI_COMM_WORLD); + //Move the remainder of the tree array forward to fill the gap that has been sent + memmove(&NhalosInTree[ntreesOffset[irecv]], &NhalosInTree[ntreesOffset[irecv]+ntrees_send], (Ntrees - ntreesOffset[irecv] - ntrees_send) * sizeof(int)); + //Remove the number of trees that have been sent from Ntrees and set ntreesToTask to zero + Ntrees -= ntrees_send; + ntreesToTask[irecv] = 0; + //Remove the number of sent trees from the offsets for the remaining tasks + for (i = irecv + 1; i < All.NTaskPerUniverse; i++) ntreesOffset[i] -= ntrees_send; + } + + //The irecv task now receives all NhalosInTree + if (ThisTask == irecv && ntrees_send > 0) { + MPI_Recv(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, isend, TAG_NHALOS, MPI_COMM_WORLD, &status); + ip = (int *) CommBuffer; + //Read CommBuffer and write to NhalosInTree + for (j = 0, i = Ntrees; j < ntrees_send; i++,j++) NhalosInTree[i] = ip[j]; + //Add the number of trees that have been sent to Ntrees + Ntrees += ntrees_send; + } + + //Now the haloes from isend to irecv + //Get number of haloes that will be sent and broadcast + nhalos_send = nhalosToTask[irecv]; + MPI_Bcast(&nhalos_send, 1, MPI_INT, isend, MPI_COMM_WORLD); + //Get number of haloes that are already at the irecv task and broadcast + nhalosInTask = Nhalos; + MPI_Bcast(&nhalosInTask, 1, MPI_INT, irecv, MPI_COMM_WORLD); + + //Check if the receiving task has enough space for the new haloes + if (nhalos_send + nhalosInTask > All.MaxHalos) + { + //If we can make space for the new trees then increase MaxHaloes and reallocate + if ( (long long)((nhalos_send + nhalosInTask - All.MaxHalos) * sizeof(struct halo) / (1.0 - 2 * ALLOC_TOLERANCE)) < (long long)(FreeBytes)) + { + All.MaxHalos += (int)(FreeBytes * (1.0 - 2 * ALLOC_TOLERANCE) / sizeof(struct halo)); + H = (struct halo *) erealloc_movable(H, All.MaxHalos * sizeof(struct halo)); + } + //Otherwise crash + else endrun("Cannot allocate enough memory for the halos to move the forests."); + } + + //Set the maximum number of haloes that can be fitted onto the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / (sizeof(struct halo)); + left_to_send = nhalos_send; + + //Loop until all haloes have been sent + do + { + //Send this turn the minimum of all haloes left to send or the maximum number that fits onto the Buffer + send_this_turn = left_to_send; + if (send_this_turn > maxlen) send_this_turn = maxlen; + + //Sending task + if (ThisTask == isend && send_this_turn > 0) + { + // Write send_this_turn haloes to CommBuffer + hp = (struct halo *) CommBuffer; + for (i = nhalosOffset[irecv] + nhalosToTask[irecv] - left_to_send; i < nhalosOffset[irecv] + nhalosToTask[irecv] - left_to_send + send_this_turn; i++) *hp++ = H[i]; + MPI_Ssend(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, irecv, TAG_HDATA, MPI_COMM_WORLD); + } + + //Receiving task + if(ThisTask == irecv && send_this_turn > 0) + { + //Receive CommBuffer from isend + MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, isend, TAG_HDATA, MPI_COMM_WORLD, &status); + //Write send_this_turn haloes in CommBuffer to end of halo array + hp = (struct halo *) CommBuffer; + for (j = 0, i = Nhalos + nhalos_send - left_to_send; j < send_this_turn; i++,j++) H[i] = hp[j]; + } + + //Haloes left to send are reduced by what has been sent this turn + left_to_send -= send_this_turn; + } + //Loop until all haloes are sent + while (left_to_send > 0); + + //The isend task now cleans up + if (ThisTask == isend && nhalos_send > 0) + { + //Move the remainder of the halo array forward to fill the gap that has been sent + memmove(&H[nhalosOffset[irecv]], &H[nhalosOffset[irecv]+nhalos_send], (Nhalos - nhalosOffset[irecv] - nhalos_send) * sizeof(struct halo)); + //Remove the number of haloes that have been sent from Ntrees and set nhalosToTask to zero + Nhalos -= nhalos_send; + nhalosToTask[irecv] = 0; + for (i = irecv + 1; i < All.NTaskPerUniverse; i++) nhalosOffset[i] -= nhalos_send; + //Set the rest of the halo array to zero + memset(&H[Nhalos], 0, (All.MaxHalos - Nhalos) * sizeof(struct halo)); + } + + //The irecv task adds the number of haloes that have been sent to Nhalos + if (ThisTask == irecv && nhalos_send > 0) Nhalos += nhalos_send; + + //Now send back from irecv to isend + //Get number of trees that will be sent and broadcast + ntrees_send = ntreesToTask[isend]; + MPI_Bcast(&ntrees_send, 1, MPI_INT, irecv, MPI_COMM_WORLD); + //Get number of trees that are already at the isend task and broadcast + ntreesInTask = Ntrees; + MPI_Bcast(&ntreesInTask, 1, MPI_INT, isend, MPI_COMM_WORLD); + + //Check if the receiving task has enough space for the new trees + if(ntreesInTask + ntrees_send > All.MaxTrees) + { + //If we need more trees than currently allocated then reallocate + All.MaxTrees = ntreesInTask + ntrees_send; + NhalosInTree = (int *) erealloc_movable(NhalosInTree, All.MaxTrees * sizeof(int)); + } + + //The irecv task sends all NhalosInTree to the isend task + if (ThisTask == irecv && ntrees_send > 0) + { + ip = (int *) CommBuffer; + //Write CommBuffer from NhalosInTree + for (i = ntreesOffset[isend]; i < ntreesOffset[isend]+ntrees_send; i++) *ip++ = NhalosInTree[i]; + //If there are trees to send then send the CommBuffer to the receiving task + MPI_Ssend(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, isend, TAG_NHALOS, MPI_COMM_WORLD); + //Move the remainder of the tree array forward to fill the gap that has been sent + memmove(&NhalosInTree[ntreesOffset[isend]], &NhalosInTree[ntreesOffset[isend]+ntrees_send], (Ntrees - ntreesOffset[isend] - ntrees_send) * sizeof(int)); + //Remove the number of trees that have been sent from Ntrees and set ntreesToTask to zero + Ntrees -= ntrees_send; + ntreesToTask[isend] = 0; + //Remove the number of sent trees from the offsets for the remaining tasks + for (i = isend + 1; i < All.NTaskPerUniverse; i++) ntreesOffset[i] -= ntrees_send; + } + + //The isend task now receives all NhalosInTree + if (ThisTask == isend && ntrees_send > 0) { + MPI_Recv(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, irecv, TAG_NHALOS, MPI_COMM_WORLD, &status); + ip = (int *) CommBuffer; + //Read CommBuffer and write to NhalosInTree + for (j = 0, i = Ntrees; j < ntrees_send; i++,j++) NhalosInTree[i] = ip[j]; + //Add the number of trees that have been sent to Ntrees + Ntrees += ntrees_send; + } + + //Now the haloes from irecv to isend + //Get number of haloes that will be sent and broadcast + nhalos_send = nhalosToTask[isend]; + MPI_Bcast(&nhalos_send, 1, MPI_INT, irecv, MPI_COMM_WORLD); + //Get number of haloes that are already at the irecv task and broadcast + nhalosInTask = Nhalos; + MPI_Bcast(&nhalosInTask, 1, MPI_INT, isend, MPI_COMM_WORLD); + + //Check if the receiving task has enough space for the new haloes + if (nhalos_send + nhalosInTask > All.MaxHalos) + { + //If we can make space for the new trees then increase MaxHaloes and reallocate + if ( (long long)((nhalos_send + nhalosInTask - All.MaxHalos) * sizeof(struct halo) / (1.0 - 2 * ALLOC_TOLERANCE)) < (long long)(FreeBytes)) + { + All.MaxHalos += (int)(FreeBytes * (1.0 - 2 * ALLOC_TOLERANCE) / sizeof(struct halo)); + H = (struct halo *) erealloc_movable(H, All.MaxHalos * sizeof(struct halo)); + } + //Otherwise free all arrays and skip the re-distribution + else endrun("Cannot allocate enough memory for the halos to move the forests."); + } + + //Set the maximum number of haloes that can be fitted onto the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / (sizeof(struct halo)); + left_to_send = nhalos_send; + + //Loop until all haloes have been sent + do + { + //Send this turn the minimum of all haloes left to send or the maximum number that fits onto the Buffer + send_this_turn = left_to_send; + if (send_this_turn > maxlen) send_this_turn = maxlen; + + //Sending task + if (ThisTask == irecv && send_this_turn > 0) + { + // Write send_this_turn haloes to CommBuffer + hp = (struct halo *) CommBuffer; + for (i = nhalosOffset[isend] + nhalosToTask[isend] - left_to_send; i < nhalosOffset[isend] + nhalosToTask[isend] - left_to_send + send_this_turn; i++) *hp++ = H[i]; + MPI_Ssend(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, isend, TAG_HDATA, MPI_COMM_WORLD); + } + + //Receiving task + if(ThisTask == isend && send_this_turn > 0) + { + //Receive CommBuffer from taskSorted[isend] + MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, irecv, TAG_HDATA, MPI_COMM_WORLD, &status); + //Write send_this_turn haloes in CommBuffer to end of halo array + hp = (struct halo *) CommBuffer; + for (j = 0, i = Nhalos + nhalos_send - left_to_send; j < send_this_turn; i++,j++) H[i] = hp[j]; + } + + //Haloes left to send are reduced by what has been sent this turn + left_to_send -= send_this_turn; + } + //Loop until all haloes are sent + while (left_to_send > 0); + + //The irecv task now cleans up + if (ThisTask == irecv && nhalos_send > 0) + { + //Move the remainder of the halo array forward to fill the gap that has been sent + memmove(&H[nhalosOffset[isend]], &H[nhalosOffset[isend]+nhalos_send], (Nhalos - nhalosOffset[isend] - nhalos_send) * sizeof(struct halo)); + //Remove the number of haloes that have been sent from Ntrees and set nhalosToTask to zero + Nhalos -= nhalos_send; + nhalosToTask[isend] = 0; + for (i = isend + 1; i < All.NTaskPerUniverse; i++) nhalosOffset[i] -= nhalos_send; + //Set the rest of the halo array to zero + memset(&H[Nhalos], 0, (All.MaxHalos - Nhalos) * sizeof(struct halo)); + } + + //The isend task adds the number of haloes that have been sent to Nhalos + if (ThisTask == isend && nhalos_send > 0) Nhalos += nhalos_send; + + }//End irecv + }//End isend + + //Free all arrays that are no longer needed + efree_movable(CommBuffer); + efree_movable(forestID); + efree_movable(nhalosOffset); + efree_movable(ntreesOffset); + efree_movable(nhalosToTask); + efree_movable(ntreesToTask); + efree_movable(forestIDTree); + efree_movable(id_list); + + //Set the values of all haloes and trees beyond the limit to zero + memset(&NhalosInTree[Ntrees], 0, (All.MaxTrees - Ntrees) * sizeof(int)); + memset(&H[Nhalos], 0, (All.MaxHalos - Nhalos) * sizeof(struct halo)); + + //Compute the total number of trees and haloes + NtreesLocal = (unsigned long long)(Ntrees); + NhalosLocal = (unsigned long long)(Nhalos); + MPI_Allreduce(&NtreesLocal, &All.TotNtrees, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&NhalosLocal, &All.TotNhalos, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + + //Copy the maximum number of trees to maxlen + MPI_Allreduce(&Ntrees, &maxlen, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + //If there are more trees than what we have now reallocate the NhalosInTree array + if (maxlen > All.MaxTrees) + { + //Leave some tolerance + All.MaxTrees = maxlen / (1.0 - 2 * ALLOC_TOLERANCE) * 2; + NhalosInTree = (int *) erealloc_movable(NhalosInTree, All.MaxTrees * sizeof(int)); + } + + //For sorting we again misuse iscale as the original index +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for schedule(static,100) +#endif + for (i = 0; i < Nhalos; i++) H[i].iscale = i; + + //Print what is done... + if (ThisTask == 0) printf("%s Sorting haloes by their forest ID...\n",All.startline); + + //Sort all haloes according to their forest ID + qsort(H, Nhalos, sizeof(struct halo), compare_forest); + + //Get new NhalosInTrees array as the old one was destroyed again in the sorting + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + NhalosInTree[itree] = H[ihalo].idesc; + ihalo += NhalosInTree[itree]; + } + + //Initialise the number of forests on each task + Nforests = 0; + nhalosInTask = 0; //Used as haloes in this forest + ntreesInTask = 0; //Used as trees in this forest + //Go through all haloes and count the number of trees and haloes in each forest + for (i = 1; i < Nhalos; i++) + { + //Increment the number of haloes in this forest + nhalosInTask++; + //If we are at the root then increment the number of trees in this forest + if (H[i].descid == 0) ntreesInTask++; + //If we arrive at a new forest + if (H[i].forestid != H[i-1].forestid) + { + //copy the number of trees and haloes to this forest + NhalosInForest[Nforests] = nhalosInTask; + NtreesInForest[Nforests] = ntreesInTask; + //Set the number of trees and haloes per forest back to zero + nhalosInTask = 0; + ntreesInTask = 0; + //Go to new forest + Nforests++; + } + } + //For the last forest we need to copy the number of trees and haloes + NhalosInForest[Nforests] = nhalosInTask + 1; + NtreesInForest[Nforests] = ntreesInTask; + //And increment the number of forests one last time + Nforests++; + + //Reset iscale imain and idesc to the default values +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for schedule(static,100) +#endif + for (i = 0; i < Nhalos; i++) + { + H[i].iscale = 0; + H[i].imain = i; + H[i].idesc = -1; + } + + //Report Memory Usage + HighMark = 0; + report_memory_usage(&HighMark, "find_forests"); + +} +#endif + + +/*! \brief This function sorts the trees or forests by their size (largest go first) + * + * Since trees and forests can get moved to other tasks to distribute the load more evenly it is best to order + * them according to their size. In this way the smallest trees or forests are in the end of the arrays and + * the number of haloes that are moved to other tasks can be set closer to the target number. + * First some extra information is added to each halo so they can be sorted. The original index is stored + * in icoprog, the size of the enitity that is being sorted (trees or forests) is stored in idescgal, and + * if the forests are being sorted the tree size is stored in imass. Then all haloes are sorted with + * compare_size according to their idescgal value and if tied according to their icoprog value. + * The arrays #NhalosInTree, #NtreesInForest, and #NhalosInForest are restored from the information given + * in the halo array. Finally the variables used for the sorting are restored (icoprog, idescgal and + * possibly imass). + */ +void sort_by_size(void) +{ + int i, itree, ihalo; +#ifdef COMPUTE_ICM + int j, imass, iforest, nHInForest, nTInForest; +#endif + + //If we do not need the ICM we can go tree-by-tree +#ifndef COMPUTE_ICM + //Loop through all trees + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + //Go through all haloes in this tree + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + { + H[ihalo].icoprog = ihalo; //Use icoprog as original index + H[ihalo].idescgal = NhalosInTree[itree]; //Use idescgal as number of haloes per tree + } + } + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Sorting trees by their size...\n",All.fullline,All.startline); + + //If we do need the ICM we must go forest-by-forest +#else + //Loop through all forests + for (iforest = 0, itree = 0, ihalo = 0; iforest < Nforests; iforest++) + { + //Loop through all trees + for (i = 0; i < NtreesInForest[iforest]; i++, itree++) + { + //Go through all haloes in this tree + for (j = 0; j < NhalosInTree[itree]; j++, ihalo++) + { + H[ihalo].icoprog = ihalo; //Use icoprog as original index + H[ihalo].idescgal = NhalosInForest[iforest]; //Use idescgal as number of haloes per forest + H[ihalo].imass = NhalosInTree[itree]; //Use imass as number of haloes per tree + } + } + } + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Sorting forests by their size...\n",All.fullline,All.startline); +#endif + + //Sort all haloes according to their tree or forest length + qsort(H, Nhalos, sizeof(struct halo), compare_size); + + //Get new NhalosInTrees array as the old one was destroyed in the sorting + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { +#ifndef COMPUTE_ICM + //If we have sorted by trees NhalosInTree is stored in idescgal + NhalosInTree[itree] = H[ihalo].idescgal; +#else + //If we have sorted by forests NhalosInTree is stored in imass + NhalosInTree[itree] = H[ihalo].imass; +#endif + ihalo += NhalosInTree[itree]; + } +#ifdef COMPUTE_ICM + //Initialise the number of forests on each task + iforest = 0; + nHInForest = 0; + nTInForest = 0; + //Go through all haloes and count the number of trees and haloes in each forest + for (i = 1; i < Nhalos; i++) + { + //Increment the number of haloes in this forest + nHInForest++; + //If we are at the root then increment the number of trees in this forest + if (H[i].descid == 0 && H[i].type != 2) nTInForest++; + //If we arrive at a new forest + if (H[i].forestid != H[i-1].forestid) + { + //copy the number of trees and haloes to this forest + NhalosInForest[iforest] = nHInForest; + NtreesInForest[iforest] = nTInForest; + //Set the number of trees and haloes per forest back to zero + nHInForest = 0; + nTInForest = 0; + //Go to new forest + iforest++; + } + } + //For the last forest we need to copy the number of trees and haloes + NhalosInForest[iforest] = nHInForest + 1; + NtreesInForest[iforest] = nTInForest; + //And increment the number of forests one last time + iforest++; + + if (iforest != Nforests) endrun("The number of forests after sorting is not the same as the stored number"); +#endif + + //Finally restore all the values that have been used for the sorting (icoprog, idescgal and possibly imass) +#ifndef COMPUTE_ICM + //Loop through all trees + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + //Go through all haloes in this tree + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + { +#else + //Loop through all forests + for (iforest = 0, ihalo = 0; iforest < Nforests; iforest++) + { + //Go through all haloes in this tree + for (i = 0, imass = 0; i < NhalosInForest[iforest]; i++, ihalo++) + { +#endif + //Reset idescgal and icoprog + H[ihalo].icoprog = -1; + H[ihalo].idescgal = H[ihalo].idesc; + //If we have also used imass restore that as well +#ifdef COMPUTE_ICM + //If this halo does not have any progenitors + if (H[ihalo].np < 1) + { + //Set the branch number for this halo (equal to the number of its leaf) + H[ihalo].imass = imass; + //Increment the number of leaves + imass++; + } + //If the halo has a descendant set its imass to this halo's imass + if (H[ihalo].idesc >= 0) H[H[ihalo].idesc].imass = H[ihalo].imass; +#endif + } + } + //For formating purposes add the two braces +#ifdef NEVERDEF +} +} +#endif + +} + + +/*! \brief This function distributes the trees to the tasks as evenly as possible + * + * The tasks are ordered by their load. Then the task containing the heaviest load sends haloes + * to tasks with the lightest load. If the sending task has sent enough haloes the next high-loaded + * task in line sends haloes. If the receiving task has received enough haloes the next low-loaded + * task in line receives haloes. Haloes are sent in packeges of full trees. + * + * \param ntask Number of tasks that the trees will be distributed over + * \param load Load of each of tasks that the trees will be distributed over + */ +void distribute_trees(int ntask, double load) +{ + int i,j,maxlen; + int nhalos_send_target,nhalos_send_actual,ntreesInTask,ntrees_send; + int isend, irecv; + int send_this_turn, left_to_send; + int *nhalosInTask, *taskSorted; + int *ip; + unsigned long long tothalos, NtreesLocal, NhalosLocal; + double totload, load_average; + double *loadInTask; + char buf[500]; + struct halo *hp; + struct task_list *taskList; + MPI_Status status; + +#ifdef COMPUTE_ICM + int Nforests_send, NforestsInTask; +#endif + + //Allocate the CommBuffer + CommBuffer = emalloc_movable(&CommBuffer,"CommBuffer", All.BufferSize * 1024 * 1024); + + //Allocate arrays to hold the information on the task number and the number of haloes it contains + nhalosInTask = emalloc_movable(&nhalosInTask,"NhalosInTask", NTask * sizeof(int)); + loadInTask = emalloc_movable(&loadInTask, "LoadInTask", NTask * sizeof(double)); + taskSorted = emalloc_movable(&taskSorted, "TaskSorted", NTask * sizeof(int)); + taskList = emalloc("TaskList", NTask * sizeof(struct task_list)); + + //Gather the number of haloes from each task and write to local task in nhalosInTask + MPI_Allgather(&Nhalos, 1, MPI_INT, nhalosInTask, 1, MPI_INT, MPI_COMM_WORLD); + MPI_Allgather(&load, 1, MPI_DOUBLE, loadInTask, 1, MPI_DOUBLE, MPI_COMM_WORLD); + + //Get the total number of haloes and the total load + NhalosLocal = (unsigned long long)(Nhalos); + MPI_Allreduce(&NhalosLocal, &tothalos, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&load, &totload, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + //Compute the average load each task should ideally hold + load_average = totload / (double)(ntask); + + //Assign the tasks to the task_list + for (i=0; i (long long)(FreeBytes)) + { + //If not then free all arrays and skip the re-distribution + if (ThisTask==0) printf("%s\n%s Cannot distribute the haloes equally given this load. Skipping the re-distribution.\n",All.fullline,All.startline); + efree(taskList); + efree_movable(taskSorted); + efree_movable(loadInTask); + efree_movable(nhalosInTask); + efree_movable(CommBuffer); + return; + } + } + //Sort the task_list according to their load + qsort(taskList, ntask, sizeof(struct task_list), compare_load); + //Assign the values in the task_list back to the arrays + for (i=0; i All.BufferSize * 1024 * 1024) + { + sprintf(buf, "CommBuffer not large enough. %d %d\n", (int) (ntrees_send * sizeof(int)), (int) (All.BufferSize * 1024 * 1024)); + endrun(buf); + } + } + + //Check if the receiving task has enough space for the new trees + if(ntreesInTask > All.MaxTrees) + { + //If we need more trees than currently allocated then reallocate + All.MaxTrees = ntreesInTask; + NhalosInTree = (int *) erealloc_movable(NhalosInTree, All.MaxTrees * sizeof(int)); +#ifdef COMPUTE_ICM + NtreesInForest = (int *) erealloc_movable(NtreesInForest, All.MaxTrees * sizeof(int)); + NhalosInForest = (int *) erealloc_movable(NhalosInForest, All.MaxTrees * sizeof(int)); +#endif + } + + //Check if the receiving task has enough space for the new haloes + if (nhalos_send_actual + nhalosInTask[irecv] > All.MaxHalos) + { + //If we can make space for the new trees then increase MaxHaloes and reallocate + if ((long long)((nhalos_send_actual + nhalosInTask[irecv] - All.MaxHalos) * sizeof(struct halo)) < (long long)(FreeBytes)) + { + All.MaxHalos = nhalos_send_actual + nhalosInTask[irecv]; + H = (struct halo *) erealloc_movable(H, All.MaxHalos * sizeof(struct halo)); + if (ThisTask==0) printf("%s Reallocating the haloes with a size of %d\n",All.startline,All.MaxHalos); + } + //Otherwise free all arrays and skip the re-distribution + else + { + if (ThisTask==0) printf("%s\n%s Cannot distribute the haloes equally given this load. Skipping the re-distribution.\n",All.fullline,All.startline); + efree_movable(taskSorted); + efree_movable(loadInTask); + efree_movable(nhalosInTask); + efree_movable(CommBuffer); + return; + } + } + + if (ThisTask == taskSorted[isend] && ntrees_send > 0) + { + //Load NhalosInTree for the trees that will be sent into the CommBuffer + ip = (int *) CommBuffer; + for (i = ntreesInTask; i < ntreesInTask+ntrees_send; i++) + { + //Write CommBuffer from NhalosInTree + *ip++ = NhalosInTree[i]; + NhalosInTree[i] = 0; + } + //If there are trees to send then send the CommBuffer to the receiving task + MPI_Ssend(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, taskSorted[irecv], TAG_NHALOSINTREE, MPI_COMM_WORLD); + +#ifdef COMPUTE_ICM + //Load NtreesInForest for the trees that will be sent into the CommBuffer + ip = (int *) CommBuffer; + for (i = NforestsInTask; i < NforestsInTask+Nforests_send; i++) + { + //Write CommBuffer from NtreesInForest + *ip++ = NtreesInForest[i]; + NtreesInForest[i] = 0; + } + //If there are forests to send then send the CommBuffer to the receiving task + MPI_Ssend(CommBuffer, (sizeof(int)) * Nforests_send, MPI_INT, taskSorted[irecv], TAG_NTREESINFOREST, MPI_COMM_WORLD); + + //Load NtreesInForest for the trees that will be sent into the CommBuffer + ip = (int *) CommBuffer; + for (i = NforestsInTask; i < NforestsInTask+Nforests_send; i++) + { + //Write CommBuffer from NhalosInForest + *ip++ = NhalosInForest[i]; + NhalosInForest[i] = 0; + } + //If there are forests to send then send the CommBuffer to the receiving task + MPI_Ssend(CommBuffer, (sizeof(int)) * Nforests_send, MPI_INT, taskSorted[irecv], TAG_NHALOSINFOREST, MPI_COMM_WORLD); +#endif + } + + //If there are trees to send then receive the CommBuffer from the sending task + if (ThisTask == taskSorted[irecv] && ntrees_send > 0) { + MPI_Recv(CommBuffer, (sizeof(int)) * ntrees_send, MPI_INT, taskSorted[isend], TAG_NHALOSINTREE, MPI_COMM_WORLD, &status); + ip = (int *) CommBuffer; + //Read CommBuffer and write to NhalosInTree + for (j = 0, i = ntreesInTask - ntrees_send; j < ntrees_send; i++,j++) NhalosInTree[i] = ip[j]; + +#ifdef COMPUTE_ICM + MPI_Recv(CommBuffer, (sizeof(int)) * Nforests_send, MPI_INT, taskSorted[isend], TAG_NTREESINFOREST, MPI_COMM_WORLD, &status); + ip = (int *) CommBuffer; + //Read CommBuffer and write to NtreesInForest + for (j = 0, i = NforestsInTask - Nforests_send; j < Nforests_send; i++,j++) NtreesInForest[i] = ip[j]; + + MPI_Recv(CommBuffer, (sizeof(int)) * Nforests_send, MPI_INT, taskSorted[isend], TAG_NHALOSINFOREST, MPI_COMM_WORLD, &status); + ip = (int *) CommBuffer; + //Read CommBuffer and write to NhalosInForest + for (j = 0, i = NforestsInTask - Nforests_send; j < Nforests_send; i++,j++) NhalosInForest[i] = ip[j]; +#endif + } + + //Set the maximum number of haloes that can be fitted onto the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / (sizeof(struct halo)); + left_to_send = nhalos_send_actual; + + //Loop until all haloes have been sent + do + { + //Send this turn the minimum of all haloes left to send or the maximum number that fits onto the Buffer + send_this_turn = left_to_send; + if (send_this_turn > maxlen) send_this_turn = maxlen; + + //if (ThisTask == 0) printf("%d %d %d\n",nhalos_send_actual,left_to_send,send_this_turn); + + //Sending task + if (ThisTask == taskSorted[isend] && send_this_turn > 0) + { + // Write send_this_turn haloes to CommBuffer + hp = (struct halo *) CommBuffer; + for (i = nhalosInTask[isend]-left_to_send; i < nhalosInTask[isend]-left_to_send+send_this_turn; i++) + *hp++ = H[i]; + memset(&H[nhalosInTask[isend]-left_to_send], 0, send_this_turn * sizeof(struct halo)); + MPI_Ssend(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, taskSorted[irecv], TAG_HDATA, MPI_COMM_WORLD); + } + + //Receiving task + if(ThisTask == taskSorted[irecv] && send_this_turn > 0) + { + //Receive CommBuffer from taskSorted[isend] + MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, taskSorted[isend], TAG_HDATA, MPI_COMM_WORLD, &status); + //Write send_this_turn haloes in CommBuffer to end of halo array + hp = (struct halo *) CommBuffer; + for (j = 0, i = nhalosInTask[irecv] + nhalos_send_actual - left_to_send; j < send_this_turn; i++,j++) H[i] = hp[j]; + } + + //Haloes left to send are reduced by what has been sent this turn + left_to_send -= send_this_turn; + } + //Loop until all haloes are sent + while (left_to_send > 0); + + //Update the number of haloes and the load in the sending and receiving task locally + nhalosInTask[isend] -= nhalos_send_actual; + nhalosInTask[irecv] += nhalos_send_actual; + loadInTask[isend] -= (double)(nhalos_send_actual)/(double)(tothalos) * totload; + loadInTask[irecv] += (double)(nhalos_send_actual)/(double)(tothalos) * totload; + //Update the number of forests/trees/haloes in the sending and receiving task + if (ThisTask == taskSorted[isend]) Ntrees = ntreesInTask; + if (ThisTask == taskSorted[irecv]) Ntrees = ntreesInTask; + if (ThisTask == taskSorted[isend]) Nhalos = nhalosInTask[isend]; + if (ThisTask == taskSorted[irecv]) Nhalos = nhalosInTask[irecv]; +#ifdef COMPUTE_ICM + if (ThisTask == taskSorted[isend]) Nforests = NforestsInTask; + if (ThisTask == taskSorted[irecv]) Nforests = NforestsInTask; +#endif + //If sending task has a lighter load than average go to next task + //If receiving task has a heavier load than average go to next task + if (loadInTask[isend] <= load_average * 1.000001) isend++; + if (loadInTask[irecv] >= load_average / 1.000001) irecv--; + } + + efree_movable(taskSorted); + efree_movable(loadInTask); + efree_movable(nhalosInTask); + efree_movable(CommBuffer); + + // Find maximum number of trees and haloes for all tasks + MPI_Allreduce(&Ntrees, &All.MaxTrees, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(&Nhalos, &All.MaxHalos, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + + // Find total number of trees and haloes for all tasks + NtreesLocal = (unsigned long long)(Ntrees); + NhalosLocal = (unsigned long long)(Nhalos); + MPI_Allreduce(&NtreesLocal, &All.TotNtrees, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&NhalosLocal, &All.TotNhalos, 1, MPI_UNSIGNED_LONG_LONG, MPI_SUM, MPI_COMM_WORLD); + + //Reallocate to decrease the memory + NhalosInTree = (int *) erealloc_movable(NhalosInTree, All.MaxTrees * sizeof(int)); +#ifdef COMPUTE_ICM + NtreesInForest = (int *) erealloc_movable(NtreesInForest, All.MaxTrees * sizeof(int)); + NhalosInForest = (int *) erealloc_movable(NhalosInForest, All.MaxTrees * sizeof(int)); +#endif + H = (struct halo *) erealloc_movable(H, (int)(All.MaxHalos) * sizeof(struct halo)); + + //Set the offsets of the first halo in the trees/forests + OffsetHalos[0] = 0; +#ifndef COMPUTE_ICM + for (i = 1; i < Ntrees; i++) OffsetHalos[i] = OffsetHalos[i-1] + NhalosInTree[i-1]; +#else + for (i = 1; i < Nforests; i++) OffsetHalos[i] = OffsetHalos[i-1] + NhalosInForest[i-1]; +#endif + + //Report memory usage + HighMark = 0; + report_memory_usage(&HighMark, "distribute_trees"); + + MPI_Barrier(MPI_COMM_WORLD); + +} + + +/*! \brief This function calculates the number of timesteps for the trees and creates a list + * + * The scale factors of all haloes in each task are sorted. Then the number of timesteps is + * determined by counting how often it increases. Further a list containing all unique timesteps + * on this task is stored. The maximum number of timesteps over all tasks is then computed and + * all timestep lists are gathered on each task. This combined list is sorted again and the + * global number of timesteps is counted as above including a global list containing all unique + * timesteps. Also the table containing the fraction of mass left between scale factors is computed. + */ +void get_timesteps(void) +{ + + int i, j, nTimesteps, maxnTimesteps; + float *a, *alist, *alistall; + char buf[500]; + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Identifying the number of timesteps and the list of scale factors from the trees...\n", All.fullline,All.startline); + + MPI_Barrier(MPI_COMM_WORLD); + + //First the arrays are allocated (MAXTIMESTEPS is used for ScaleFactor initially) + ScaleFactor = emalloc_movable(&ScaleFactor,"ScaleFactor", MAXTIMESTEPS * sizeof(float)); + a = emalloc("ScaleFactorHalo", All.MaxHalos * sizeof(float)); + alist = emalloc("ScaleFactorList", sizeof(float)); + + //Write scale factors of all trees in array +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for +#endif + for (i=0; ia[i-1]) { + nTimesteps++; + //Increase the size of alist + alist = (float *) erealloc(alist, nTimesteps * sizeof(float)); + //Write the new unique scale factor to alist + alist[nTimesteps-1] = a[i]; + } + } + + //Get the maximum number of timesteps across all tasks + MPI_Allreduce(&nTimesteps, &maxnTimesteps, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + + //Increase the size of alist to maxnTimesteps and fill with zeros + alist = (float *) erealloc(alist, maxnTimesteps * sizeof(float)); + for (i=nTimesteps; ialistall[i-1]) + { + All.NTimesteps++; + //Write the new unique scale factor to the global list + ScaleFactor[All.NTimesteps-1] = alistall[i]; + } + } + + //Print the number of timesteps and the starting and final scale factor + if (ThisTask == 0) printf("%s There are %d timesteps ranging from a=%f to a=%f\n",All.startline,All.NTimesteps,ScaleFactor[0],ScaleFactor[All.NTimesteps-1]); + + //Check that the number of Timesteps does not exceed the maximum + if (All.NTimesteps > MAXTIMESTEPS) + { + sprintf(buf, "The number of timesteps is %d which is larger than the maximum allowed number of %d.", nTimesteps, MAXTIMESTEPS); + endrun(buf); + } + + //Check that the number of Timesteps is not too low (at least 4 for the splines - though in practice much higher) + if (All.NTimesteps < 4) + { + sprintf(buf, "The number of timesteps is %d which is lower than the minimim allowed number of 4.", nTimesteps); + endrun(buf); + } + + efree(alistall); + efree(alist); + efree(a); + + //Reallocate with a size of the list of the actual global number of timesteps + ScaleFactor = (float *) erealloc_movable(ScaleFactor, All.NTimesteps * sizeof(float)); + + CosmicTime = emalloc_movable(&CosmicTime,"CosmicTime", All.NTimesteps * sizeof(float)); + Timestep = emalloc_movable(&Timestep, "Timestep", All.NTimesteps * sizeof(float)); + DynTime = emalloc_movable(&DynTime, "DynamicalTime", All.NTimesteps * sizeof(float)); + + for (i = 0; i < All.NTimesteps; i++) + { + CosmicTime[i] = cosmictime(ScaleFactor[i]); + if (i == 0) Timestep[i] = CosmicTime[i]; + else Timestep[i] = CosmicTime[i] - CosmicTime[i-1]; + DynTime[i] = tdyn(ScaleFactor[i]); + } + + //Loop through all haloes and set its iscale to the corresponding index in ScaleFactor +#if defined(_OPENMP) && (OPENMPTHREADS > 1) +#pragma omp parallel for +#endif + for (i=0; i= All.NTimesteps) + endrun("Halo scale factor cannot be found in the list."); + } + + //Go through all output redshifts and find the corresponding snapshot number + for (i = 0; i < All.Noutputredshifts; i++) + { //Find the index for this output scale factor + for (j = 0; j < All.NTimesteps; j++) + { + if (ScaleFactor[j] >= 1./(OutputRedshifts[i]+1.0)) break; + } + if (j == 0) Output_iscale[i] = 0; + else + { + if (ScaleFactor[j]-1./(OutputRedshifts[i]+1.0) >= 1./(OutputRedshifts[i]+1.0)-ScaleFactor[j-1]) + Output_iscale[i] = j-1; + else Output_iscale[i] = j; + } + } + +#ifdef WRITE_MAINBRANCH + for (j = 0; j < All.NTimesteps; j++) + { + if (ScaleFactor[j] >= 1./(All.mainBranchRedshift+1.0)) break; + } + if (j == 0) All.MainBranch_iscale = 0; + else + { + if (ScaleFactor[j]-1./(All.mainBranchRedshift+1.0) >= 1./(All.mainBranchRedshift+1.0)-ScaleFactor[j-1]) + All.MainBranch_iscale = j-1; + else All.MainBranch_iscale = j; + } +#endif + +#ifdef READ_WP + //Find the index for the correlation function scale factor + for (i = 0; i < All.NTimesteps; i++) + { + if (ScaleFactor[i] >= 1./(All.wpredshift+1.0)) break; + } + if (i == 0) All.Wpscale = 0; + else + { + if (ScaleFactor[i]-1./(All.wpredshift+1.0) >= 1./(All.wpredshift+1.0)-ScaleFactor[i-1]) All.Wpscale = i-1; + else All.Wpscale = i; + } +#endif + + //Compute the MassLeft table which stores the fraction of mass left after any time interval + MassLeft = emalloc_movable(&MassLeft,"MassLeft", All.NTimesteps * All.NTimesteps * sizeof(float)); + for (i = 0; i < All.NTimesteps; i++) + { + for (j = 0; j < All.NTimesteps; j++) + { + if (j < i) MassLeft[i*All.NTimesteps+j] = 1.0 - 0.05 * log( ((CosmicTime[i]-CosmicTime[j])*All.t_unit/1.4e6) + 1.0); + else MassLeft[i*All.NTimesteps+j] = 1.0; + } + } + + //Broadcast so that every universe has the joy + MPI_Bcast(ScaleFactor, All.NTimesteps, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(CosmicTime, All.NTimesteps, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(Timestep, All.NTimesteps, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(DynTime, All.NTimesteps, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(MassLeft, All.NTimesteps * All.NTimesteps, MPI_FLOAT, 0, MPI_COMM_WORLD); + MPI_Bcast(Output_iscale, All.Noutputredshifts, MPI_INT, 0, MPI_COMM_WORLD); + MPI_Bcast(OutputRedshifts, All.Noutputredshifts, MPI_FLOAT, 0, MPI_COMM_WORLD); + +} + + +/*! \brief This function adds orphans to each tree + * + * First the total number of orphans on each task is computed by summing the number of + * timesteps left for each leaf. Then the halo arrays is reallocated to make space for + * the orphans. The maximum ID is calculated throughout the simulation and is used as + * the first orphan ID. After the number of orphans in each tree is computed we shift + * the haloes in the memory for each tree to make space for the orphans. We then go + * through all leaves and detach all merging haloes from their branch and add orphans + * as their descendants. Finally the orphans are sorted into each tree by scale factor. + */ +void add_orphans(void) +{ + int i,j,ihalo,iorphan,itree,idesc,ithis; + int nhalos_cumulative, norphans_cumulative; + int *nOrphansInTree, *nOrphansInTask; + IDType maxid, maxhaloIDalltasks; + +#ifdef COMPUTE_ICM + int iforest = 0; +#endif + + //Calculate the number of orphans on this task and the maximum over all tasks + Norphans = 0; + for (i = 0; i < Nhalos; i++) if (H[i].mmp==0) Norphans += All.NTimesteps - H[i].iscale - 1; + MPI_Allreduce(&Norphans, &All.MaxOrphans, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Inserting %12d possible orphans in each tree...\n", All.fullline,All.startline,All.MaxOrphans); + + //Calculate the maximum number of haloes including the orphans and reallocate the halo array + All.MaxHalos = All.MaxHalos + All.MaxOrphans; + H = (struct halo *) erealloc_movable(H, All.MaxHalos * sizeof(struct halo)); + memset(&H[Nhalos], 0, (All.MaxHalos-Nhalos) * sizeof(struct halo)); + + //Check maximum halo ID in tree + maxid = H[0].haloid; + for (i = 0; i < Nhalos; i++) if (H[i].haloid > maxid) maxid = H[i].haloid; + +#ifndef LONGIDS + //If the new IDs cannot be represented by IDType end the programme + if ((unsigned long long)(maxid) + (unsigned long long)(All.MaxOrphans) > (1ULL << (sizeof(IDType)*8)) - 1ULL && ThisTask==0) endrun("The bit size of the IDType is not large enough to hold all IDs. Use LONGIDS!"); +#endif + + // Find maximum halo ID across all tasks + maxhaloIDalltasks = 0; //probably not needed +#ifndef LONGIDS + MPI_Allreduce(&maxid, &maxhaloIDalltasks, 1, MPI_UNSIGNED, MPI_MAX, MPI_COMM_WORLD); +#else + MPI_Allreduce(&maxid, &maxhaloIDalltasks, 1, MPI_UNSIGNED_LONG_LONG, MPI_MAX, MPI_COMM_WORLD); +#endif + maxid = maxhaloIDalltasks; + + //Allocate and compute array to store the number of orphans in each task + nOrphansInTask = emalloc("NOrphansInTask", NTask * sizeof(int)); + MPI_Allgather(&Norphans, 1, MPI_INT, nOrphansInTask, 1, MPI_INT, MPI_COMM_WORLD); + + //For each task determine the starting orphan id + for (i=0; ii) maxid += nOrphansInTask[i]; + } + + //Allocate and compute array to store the number of orphans in each tree + nOrphansInTree = emalloc("NOrphansInTree", Ntrees * sizeof(int)); + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + nOrphansInTree[itree] = 0; + //Now all the haloes in the tree + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + { + if (H[ihalo].mmp==0) nOrphansInTree[itree] += All.NTimesteps - H[ihalo].iscale - 1; + } + } + + //Initialise the cumulative numbers of haloes and orphans + nhalos_cumulative = norphans_cumulative = 0; + + //Go through the trees backwards + for (itree = Ntrees-1; itree >= 0; itree--) + { + //Get the cumulative numbers of haloes and orphans up to this tree + nhalos_cumulative += NhalosInTree[itree]; + norphans_cumulative += nOrphansInTree[itree]; + //Move the haloes in this tree towards the end of the array and leave space for the orphans + memmove(&H[Nhalos + Norphans - nhalos_cumulative - norphans_cumulative], &H[Nhalos-nhalos_cumulative], NhalosInTree[itree] * sizeof(struct halo)); + //If there are orphans + if (nOrphansInTree[itree]>0) + { + //Set all orphans to zero + memset(&H[Nhalos + Norphans - nhalos_cumulative - norphans_cumulative + NhalosInTree[itree]], 0, nOrphansInTree[itree] * sizeof(struct halo)); + } + } + + //Set the orphan index to zero + iorphan = 0; + + //Now go through all trees separately + for (itree = 0, ihalo = 0; itree < Ntrees; itree++) + { + //increment the orphan index to be the first after all regular haloes + iorphan += NhalosInTree[itree]; + + //Go through all haloes in this tree + for (i = 0; i < NhalosInTree[itree]; i++, ihalo++) + { + //Set the halo type to 0 for main haloes (upid==0) and to 1 for subhaloes (otherwise) + if (H[ihalo].upid == 0) H[ihalo].type = 0; + else H[ihalo].type = 1; + + //Create a new orphan if the halo is not the most massive progenitor i.e. merging + if (H[ihalo].mmp==0) + { + //Go from this halo down to its descendant for which the halo ID is this halo's descendant ID + idesc = ihalo; + while (H[idesc].haloid != H[ihalo].descid) idesc++; + + //Go from the descendant up to its progenitor for which the descendant ID is this halo's ID + ithis = idesc; + while (H[ithis].descid != H[idesc].haloid) ithis--; + + //Detach the halos from the larger branch + //Descendant ID is now the orphan which is set t maxid+1 + H[ihalo].descid = maxid+1; + //The halo is now the MMP (of the orphan) + H[ihalo].mmp = 1; + //The previous descendant has now only one progenitor + H[idesc].np = 1; + + //Now at the next timestep we create the orphan + H[iorphan].haloid = maxid+1; + //Unless its true descendant is 0 create the next orphan descendant + if (H[idesc].descid==0) H[iorphan].descid = 0; + else H[iorphan].descid = maxid+2; + //We now use the upid of the orphan to record the true descendants halo ID + H[iorphan].upid = H[idesc].haloid; + + //Copy all relevant information from the true descendant + H[iorphan].iscale = H[idesc].iscale; + H[iorphan].type = 2; //All orphans are type 2 + H[iorphan].np = 1; //The orphans can never be leaves and nothing merges into them + H[iorphan].mmp = 1; //Always the main progenitor + H[iorphan].a = H[idesc].a; +#ifdef ORPHAN_MASSLOSS + H[iorphan].mvir = 0; +#else + H[iorphan].mvir = H[ihalo].mvir; +#endif + H[iorphan].rvir = H[ihalo].rvir; + H[iorphan].c = H[ihalo].c; + H[iorphan].lambda = H[ihalo].lambda; + H[iorphan].vel[0] = H[idesc].vel[0]; + H[iorphan].vel[1] = H[idesc].vel[1]; + H[iorphan].vel[2] = H[idesc].vel[2]; +#ifdef COMPUTE_ICM + H[iorphan].forestid = H[idesc].forestid; +#endif + + //The first orphan is set - increment the iorphan index and ID + iorphan++; + maxid++; + + //Now do create all remaining orphans until the final timestep + for (j=H[ihalo].iscale+2; j H[H[ihalo].impeak].mvir) H[ihalo].impeak = ihalo; + + //Determine the baryonic accretion rate + if (H[ihalo].iprog == -1) + { + //If the halo does not have a progenitor the rate is mvir/t_cosmic*fb + H[ihalo].mdotbary = H[ihalo].mvir / CosmicTime[H[ihalo].iscale] * All.f_baryon; + } + else + //If there is a progenitor go back to the progenitor that has a cosmic time + dynamical time equal to this halos cosmic time + { + //Identify the progenitor (cannot be -1) + iprog = H[ihalo].iprog; + //Check if we are at least one dynamical time back + while (CosmicTime[H[iprog].iscale] + DynTime[H[iprog].iscale] >= CosmicTime[H[ihalo].iscale]) + { + //Save current progenitor index and compute next progenitor index + ithis = iprog; + iprog = H[iprog].iprog; + //If we are at a leave set the progenitor index to the leaf index and stop + if (iprog < 0) + { + iprog = ithis; + break; + } + } + //Set the baryonic accretion rate to (mvir_j-mvir_i)/(t_j-t_i)*fbary where i_j>t_i+tdyn_i + H[ihalo].mdotbary = (H[ihalo].mvir - H[iprog].mvir) / (CosmicTime[H[ihalo].iscale] - CosmicTime[H[iprog].iscale]) * All.f_baryon; + //If the accretion rate is negative set it to 0 + H[ihalo].mdotbary = max(H[ihalo].mdotbary,0.0); + }//End computation of accretion rate + + }//End loop through all haloes + + //And print the memory usage + report_memory_usage(&HighMark, "Compute_Halo_History"); + +} + + +#ifdef COMPUTE_ICM +/*! \brief This function links all haloes to their parents + * + * All halo IDs and indices are stored in an id_list and sorted by their ID. For each halo it + * is checked if it is a main halo (then the host index is its own index) or if it is a subhalo. + * In the latter case the id_list on this task is searched for the UPID. If it is found then the + * host id is set equal to the index of the parent. + */ +void find_parents(void) +{ + int i,ihalo,iforest,ihost,nlist; + struct search_list_IDs *id_list; + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Linking the haloes to their parents...\n", All.fullline,All.startline); + MPI_Barrier(MPI_COMM_WORLD); + + //Allocate the memory for some arrays. The list id_on_other_task is allocated in increments of allocsize + id_list = emalloc("ID_LIST", All.MaxHalos * sizeof(struct search_list_IDs)); + + //Go through each forest + for (iforest = 0, ihalo = 0; iforest < Nforests; iforest++) + { + + //Write all IDs and indices of the haloes in this forest to a list + for (i = 0, nlist = 0; i < NhalosInForest[iforest]; i++) + { + if (H[ihalo+i].type==0 || H[ihalo+i].type==1) + { + id_list[nlist].position = ihalo + i; + id_list[nlist].ID = H[ihalo+i].haloid; + nlist++; + } + } + + //Sorting the ID list on each task using the function compare_id + qsort(id_list, nlist, sizeof(struct search_list_IDs), compare_id); + + //Go through all haloes in this forest + for (i = 0; i < NhalosInForest[iforest]; i++, ihalo++) + { + //If the upid is 0 it is a host halo - set the host index equal to the current index + if (H[ihalo].upid == 0) H[ihalo].ihost = ihalo; + //This is a subhalo so search for its parent + else + { + //Search the local ID list for the upid and return the index of the parent + ihost = binary_search_id(id_list, nlist, H[ihalo].upid); + //If we find a parent set the host index equal to the parent's index + if (ihost >= 0) H[ihalo].ihost = ihost; + //If we cannot find the parent on this task crash + else endrun("Cannot find parent on this task."); + } + + }//End haloes + }//End forests + + efree(id_list); + + report_memory_usage(&HighMark, "Find_Parents"); + +} +#endif + + +/*! \brief This function copies the merger trees to the tasks for all universes + * + * As the merger trees are only read by and distributed over the tasks for the first universe, + * we need to copy the trees to all other tasks. This needs to be done such that the haloes are + * on the same tasks modulo the universe. It is first checked that the number of tasks * universes + * is equal to or less than the total number of tasks. All tasks on the first universe then copy + * their data (Ntrees, Nhalos, NhalosInTree, H) to the tasks that hold universe nuniverses/2. + * The maximum universe that needs to be addressed in following calls is copied as well. Each + * universe that received data then copies this data to other universes as well, until all universes + * have received all data. The Haloes are copied into the CommBuffer which is then sent to the + * other task. + * + * \param ntask Number of tasks needed for one universe + * \param nuniverses Number of universes that shall be processed + */ +void copy_trees_to_other_universes(int ntask, int nuniverses) +{ + int i, j, maxlen, send_this_turn, left_to_send, has_data, to_universe, to_task, from_task, max_universe; + struct halo *hp; + MPI_Status status; + + //Check if there are enough tasks to hold all universes + if (ntask * nuniverses > NTask) endrun("Not enough tasks to hold all universes."); + + //Print what is done... + if (ThisTask == 0) printf("%s\n%s Copying the merger trees to other universes...\n", All.fullline,All.startline); + MPI_Barrier(MPI_COMM_WORLD); + + //Communicate all global parameters to the other processes + MPI_Bcast(&All, sizeof(struct global_data), MPI_BYTE, 0, MPI_COMM_WORLD); + + //Allocate the CommBuffer + CommBuffer = emalloc("CommBuffer", All.BufferSize * 1024 * 1024); + + //Set the maximum number of haloes that can be fitted onto the CommBuffer + maxlen = ((int) (All.BufferSize * 1024 * 1024)) / (sizeof(struct halo)); + + //Initialise values for all universe + has_data = max_universe = to_universe = 0; + + //Set values for the first universe that holds all data initially + if (MasterTask == 0) + { + has_data = 1; + max_universe = nuniverses; + to_universe = max_universe/2; + } + + //Loop until the tasks would send their data to themselves + while (to_universe * ntask != MasterTask) + { //Sending universes (tasks) + if (has_data) + { //Set index of task that this task is sending the data to + to_task = to_universe * ntask + ThisTask - MasterTask; + //Print which universe is dealt with now + if (ThisTask == MasterTask) printf("%s Copying trees from universe %4d to universe %4d on tasks %5d to %5d.\n", All.startline,MasterTask/ntask,to_universe,to_universe*ntask,(to_universe+1)*ntask-1); + MPI_Ssend(&max_universe, 1, MPI_INT, to_task, TAG_REQUEST, MPI_COMM_WORLD); + //Send Ntrees, Nhalos, NhalosInTree, Nforests, NtreesInForest, and NhalosInForest + MPI_Ssend(&Ntrees, 1, MPI_INT, to_task, TAG_NTREES, MPI_COMM_WORLD); + MPI_Ssend(&Nhalos, 1, MPI_INT, to_task, TAG_NHALOS, MPI_COMM_WORLD); + MPI_Ssend(NhalosInTree, Ntrees, MPI_INT, to_task, TAG_NHALOSINTREE, MPI_COMM_WORLD); + printf("%s Task %d to task %d: %d trees, %d haloes\n",All.startline,ThisTask,to_task,Ntrees,Nhalos); +#ifdef COMPUTE_ICM + MPI_Ssend(&Nforests, 1, MPI_INT, to_task, TAG_NFORESTS, MPI_COMM_WORLD); + MPI_Ssend(NtreesInForest, Nforests, MPI_INT, to_task, TAG_NTREESINFOREST, MPI_COMM_WORLD); + MPI_Ssend(NhalosInForest, Nforests, MPI_INT, to_task, TAG_NHALOSINFOREST, MPI_COMM_WORLD); +#endif + MPI_Ssend(&Norphans, 1, MPI_INT, to_task, TAG_NORPHANS, MPI_COMM_WORLD); +#ifdef COMPUTE_ICM + MPI_Ssend(OffsetHalos, Nforests, MPI_INT, to_task, TAG_OFFSETHALOS, MPI_COMM_WORLD); +#else + MPI_Ssend(OffsetHalos, Ntrees, MPI_INT, to_task, TAG_OFFSETHALOS, MPI_COMM_WORLD); +#endif + //Number of haloes that we want to send (all of them) + left_to_send = Nhalos; + //Loop until all haloes have been sent + do + { //Send this turn the minimum of all haloes left to send or the maximum number that fits onto the Buffer + send_this_turn = left_to_send; + if (send_this_turn > maxlen) send_this_turn = maxlen; + //Sending task + if (send_this_turn > 0) + { // Write send_this_turn haloes to CommBuffer and send to universe * ntask + task + hp = (struct halo *) CommBuffer; + for (i = Nhalos - left_to_send; i < Nhalos - left_to_send + send_this_turn; i++) + *hp++ = H[i]; + MPI_Send(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, to_universe*ntask+ThisTask-MasterTask, TAG_HDATA, MPI_COMM_WORLD); + } + //Haloes left to send are reduced by what has been sent this turn + left_to_send -= send_this_turn; + } + //Loop until all haloes are sent + while (left_to_send > 0); + //Set new maximum universe and compute the new universe the data is sent to + max_universe = to_universe; + to_universe = (MasterTask/ntask+max_universe)/2; + } + //This universe has no data yet and therefore receives data + else + { //Wait until we get a message from the sending universe / task + MPI_Probe(MPI_ANY_SOURCE, TAG_REQUEST, MPI_COMM_WORLD, &status); + //Determine task that is sending the data + from_task = status.MPI_SOURCE; + //Receive the maximum universe this universe / task has to deal with + MPI_Recv(&max_universe, 1, MPI_INT, from_task, TAG_REQUEST, MPI_COMM_WORLD, &status); + //Receive Ntrees, Nhalos, NhalosInTree, Nforests, NtreesInForest, and NhalosInForest + MPI_Recv(&Ntrees, 1, MPI_INT, from_task, TAG_NTREES, MPI_COMM_WORLD, &status); + MPI_Recv(&Nhalos, 1, MPI_INT, from_task, TAG_NHALOS, MPI_COMM_WORLD, &status); + MPI_Recv(NhalosInTree, Ntrees, MPI_INT, from_task, TAG_NHALOSINTREE, MPI_COMM_WORLD, &status); +#ifdef COMPUTE_ICM + MPI_Recv(&Nforests, 1, MPI_INT, from_task, TAG_NFORESTS, MPI_COMM_WORLD, &status); + MPI_Recv(NtreesInForest, Nforests, MPI_INT, from_task, TAG_NTREESINFOREST, MPI_COMM_WORLD, &status); + MPI_Recv(NhalosInForest, Nforests, MPI_INT, from_task, TAG_NHALOSINFOREST, MPI_COMM_WORLD, &status); +#endif + MPI_Recv(&Norphans, 1, MPI_INT, from_task, TAG_NORPHANS, MPI_COMM_WORLD, &status); +#ifdef COMPUTE_ICM + MPI_Recv(OffsetHalos, Nforests, MPI_INT, from_task, TAG_OFFSETHALOS, MPI_COMM_WORLD, &status); +#else + MPI_Recv(OffsetHalos, Ntrees, MPI_INT, from_task, TAG_OFFSETHALOS, MPI_COMM_WORLD, &status); +#endif + //Number of haloes that we want to send (all of them) + left_to_send = Nhalos; + //Loop until all haloes have been sent + do + { //Send this turn the minimum of all haloes left to send or the maximum number that fits onto the Buffer + send_this_turn = left_to_send; + if (send_this_turn > maxlen) send_this_turn = maxlen; + //Receiving task + if(send_this_turn > 0) + { //Receive CommBuffer from task + MPI_Recv(CommBuffer, (sizeof(struct halo)) * send_this_turn, MPI_BYTE, from_task, TAG_HDATA, MPI_COMM_WORLD, &status); + //Write send_this_turn haloes in CommBuffer to end of halo array + hp = (struct halo *) CommBuffer; + for (i = Nhalos - left_to_send, j = 0; i < Nhalos - left_to_send + send_this_turn; i++, j++) + H[i] = hp[j]; + } + //Haloes left to send are reduced by what has been sent this turn + left_to_send -= send_this_turn; + } + //Loop until all haloes are sent + while (left_to_send > 0); + //Compute the new universe the data is sent to and remember that this universe / task now has data + to_universe = (MasterTask/ntask+max_universe)/2; + has_data = 1; + } //End sending and receiving options + } //End loop through universes + + //Free the CommBuffer + efree(CommBuffer); + +} diff --git a/src/setup.c b/src/setup.c new file mode 100644 index 0000000..05cca9b --- /dev/null +++ b/src/setup.c @@ -0,0 +1,930 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code - File setup.c // +// Parts of these functions have been adapted from the GADGET code developed by Volker Springel // +/////////////////////////////////////////////////////////////////////////////////////////////////// +/// +/// \file setup.c +/// \brief Contains functions that initialise the emerge code and read the input parameters +/// +/// This file contains all functions that are needed to read the parameters and initialuse all +/// variables that are needed to run emerge. It also contains all functions needed to finalise +/// the code. +/// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + + +/*! \brief This function parses the parameter file. + * + * Each parameter is defined by a keyword (`tag'), and can be either + * of type douple, int, or character string. Three arrays containing the name, + * type and address of the parameter are filled first. The routine then parses + * the parameter file and fills the referenced variables. The routine makes sure that + * each parameter appears exactly once in the parameter file, otherwise + * error messages are produced that complain about the missing parameters. + * Basic checks are performed on the supplied parameters in the end. + * + * \param fname The file name of the parameter file + */ +void read_parameterfile(char *fname) +{ + +#define FLOAT 1 ///< Tag for floating point numbers +#define STRING 2 ///< Tag for strings +#define INT 3 ///< Tag for integers +#define MAXTAGS 300 ///< Maximum number of entries the parameter file can contain + + FILE *fd; + char buf[200], buf1[200], buf2[200], buf3[200]; + int i, j, nt; + int id[MAXTAGS]; + void *addr[MAXTAGS]; + char tag[MAXTAGS][50]; + int errorFlag = 0; + + // Check sizes of types on this machine + if(sizeof(long long) != 8) endrun("Type `long long' is not 64 bit on this platform."); + if(sizeof(int) != 4) endrun("Type `int' is not 32 bit on this platform."); + if(sizeof(float) != 4) endrun("Type `float' is not 32 bit on this platform."); + if(sizeof(double) != 8) endrun("Type `double' is not 64 bit on this platform."); + + // Read parameter file on process 0 + if(ThisTask == 0) + { + + nt = 0; + + strcpy(tag[nt], "TreefileName"); + addr[nt] = All.treefile_name; + id[nt++] = STRING; + + strcpy(tag[nt], "MaxMemSize"); + addr[nt] = &All.MaxMemSize; + id[nt++] = INT; + + strcpy(tag[nt], "BufferSize"); + addr[nt] = &All.BufferSize; + id[nt++] = INT; + + strcpy(tag[nt], "UniversesInParallel"); + addr[nt] = &All.NUniverses; + id[nt++] = INT; + + strcpy(tag[nt], "NumOutputFiles"); + addr[nt] = &All.NumOutputFiles; + id[nt++] = INT; + + strcpy(tag[nt], "NumFilesInParallel"); + addr[nt] = &All.NumFilesInParallel; + id[nt++] = INT; + + strcpy(tag[nt], "OutputFormat"); + addr[nt] = &All.OutputFormat; + id[nt++] = INT; + + strcpy(tag[nt], "MCMCseed"); + addr[nt] = &All.Seed; + id[nt++] = INT; + + strcpy(tag[nt], "NumberOfMCMCWalkers"); + addr[nt] = &All.Nwalkers; + id[nt++] = INT; + + strcpy(tag[nt], "ModelName"); + addr[nt] = All.model_name; + id[nt++] = STRING; + + strcpy(tag[nt], "MCMCScaleParameter"); + addr[nt] = &All.mcmca; + id[nt++] = FLOAT; + + strcpy(tag[nt], "ChainTemperature"); + addr[nt] = &All.temperature; + id[nt++] = FLOAT; + + strcpy(tag[nt], "TimeLimit"); + addr[nt] = &All.timelimit; + id[nt++] = FLOAT; + + strcpy(tag[nt], "OutputRedshifts"); + addr[nt] = All.output_redshifts; + id[nt++] = STRING; + + strcpy(tag[nt], "OutputMassThreshold"); + addr[nt] = &All.minmass; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Verbose"); + addr[nt] = &All.verbose; + id[nt++] = INT; + + strcpy(tag[nt], "HubbleParam"); + addr[nt] = &All.h_100; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Omega0"); + addr[nt] = &All.Omega_0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "OmegaLambda"); + addr[nt] = &All.Omega_Lambda_0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "OmegaBaryon"); + addr[nt] = &All.Omega_Baryon_0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "BoxSize"); + addr[nt] = &All.Lbox; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Mstar_min"); + addr[nt] = &All.mstarmin; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Mstar_max"); + addr[nt] = &All.mstarmax; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Delta_Mstar"); + addr[nt] = &All.dmstar; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Observation_Error_0"); + addr[nt] = &All.obssigma0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Observation_Error_z"); + addr[nt] = &All.obssigmaz; + id[nt++] = FLOAT; + + strcpy(tag[nt], "UnitLength_in_Mpc"); + addr[nt] = &All.x_unit; + id[nt++] = FLOAT; + + strcpy(tag[nt], "UnitTime_in_yr"); + addr[nt] = &All.t_unit; + id[nt++] = FLOAT; + + strcpy(tag[nt], "UnitMass_in_Msun"); + addr[nt] = &All.m_unit; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Eff_MassPeak"); + addr[nt] = &All.M0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Eff_Normalisation"); + addr[nt] = &All.Epsilon0; + id[nt++] = FLOAT; + +#ifdef SFE_MPEAK_ZEVOLV + strcpy(tag[nt], "Eff_MassPeak_Z"); + addr[nt] = &All.MZ; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_NORM_ZEVOLV + strcpy(tag[nt], "Eff_Normalisation_Z"); + addr[nt] = &All.EpsilonZ; + id[nt++] = FLOAT; +#endif + + strcpy(tag[nt], "Eff_LowMassSlope"); + addr[nt] = &All.Beta0; + id[nt++] = FLOAT; + +#ifndef SFE_SAME_SLOPE + strcpy(tag[nt], "Eff_HighMassSlope"); + addr[nt] = &All.Gamma0; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_BETA_ZEVOLV + strcpy(tag[nt], "Eff_LowMassSlope_Z"); + addr[nt] = &All.BetaZ; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + strcpy(tag[nt], "Eff_HighMassSlope_Z"); + addr[nt] = &All.GammaZ; + id[nt++] = FLOAT; +#endif +#endif + + strcpy(tag[nt], "Fraction_Escape_ICM"); + addr[nt] = &All.Fesc; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Fraction_Stripping"); + addr[nt] = &All.Fstrip; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Timescale_Quenching"); + addr[nt] = &All.Tau0; + id[nt++] = FLOAT; + +#ifdef SAT_QUENCH_MASS_DEPENDENT + strcpy(tag[nt], "Slope_Quenching"); + addr[nt] = &All.TauS; + id[nt++] = FLOAT; +#endif + +#ifdef SAT_SFR_EXP_DECAY + strcpy(tag[nt], "Decay_Quenching"); + addr[nt] = &All.TauD; + id[nt++] = FLOAT; +#endif + + strcpy(tag[nt], "Eff_MassPeak_Range"); + addr[nt] = &All.DeltaM0; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Eff_Normalisation_Range"); + addr[nt] = &All.DeltaEpsilon0; + id[nt++] = FLOAT; + +#ifdef SFE_MPEAK_ZEVOLV + strcpy(tag[nt], "Eff_MassPeak_Z_Range"); + addr[nt] = &All.DeltaMZ; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_NORM_ZEVOLV + strcpy(tag[nt], "Eff_Normalisation_Z_Range"); + addr[nt] = &All.DeltaEpsilonZ; + id[nt++] = FLOAT; +#endif + + strcpy(tag[nt], "Eff_LowMassSlope_Range"); + addr[nt] = &All.DeltaBeta0; + id[nt++] = FLOAT; + +#ifndef SFE_SAME_SLOPE + strcpy(tag[nt], "Eff_HighMassSlope_Range"); + addr[nt] = &All.DeltaGamma0; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_BETA_ZEVOLV + strcpy(tag[nt], "Eff_LowMassSlope_Z_Range"); + addr[nt] = &All.DeltaBetaZ; + id[nt++] = FLOAT; +#endif + +#ifdef SFE_GAMMA_ZEVOLV +#ifndef SFE_SAME_SLOPE + strcpy(tag[nt], "Eff_HighMassSlope_Z_Range"); + addr[nt] = &All.DeltaGammaZ; + id[nt++] = FLOAT; +#endif +#endif + + strcpy(tag[nt], "Fraction_Escape_ICM_Range"); + addr[nt] = &All.DeltaFesc; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Fraction_Stripping_Range"); + addr[nt] = &All.DeltaFstrip; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Timescale_Quenching_Range"); + addr[nt] = &All.DeltaTau0; + id[nt++] = FLOAT; + +#ifdef SAT_QUENCH_MASS_DEPENDENT + strcpy(tag[nt], "Slope_Quenching_Range"); + addr[nt] = &All.DeltaTauS; + id[nt++] = FLOAT; +#endif + +#ifdef SAT_SFR_EXP_DECAY + strcpy(tag[nt], "Decay_Quenching_Range"); + addr[nt] = &All.DeltaTauD; + id[nt++] = FLOAT; +#endif + +#ifdef READ_SMF + strcpy(tag[nt], "SMFfileName"); + addr[nt] = All.smffile_name; + id[nt++] = STRING; +#endif + +#ifdef READ_FQ + strcpy(tag[nt], "FQfileName"); + addr[nt] = All.fqfile_name; + id[nt++] = STRING; +#endif + +#ifdef READ_CSFRD + strcpy(tag[nt], "CSFRDfileName"); + addr[nt] = All.csfrdfile_name; + id[nt++] = STRING; +#endif + +#ifdef READ_SSFR + strcpy(tag[nt], "SSFRfileName"); + addr[nt] = All.ssfrfile_name; + id[nt++] = STRING; +#endif + +#ifdef READ_WP + strcpy(tag[nt], "WPfileName"); + addr[nt] = All.wpfile_name; + id[nt++] = STRING; +#endif + +#ifdef GLOBAL_SIGMA_SMF + strcpy(tag[nt], "Global_Sigma_SMF_LZ"); + addr[nt] = &All.GlobalSigmaSmfLz; + id[nt++] = FLOAT; + + strcpy(tag[nt], "Global_Sigma_SMF_HZ"); + addr[nt] = &All.GlobalSigmaSmfHz; + id[nt++] = FLOAT; +#endif + +#ifdef GLOBAL_SIGMA_FQ + strcpy(tag[nt], "Global_Sigma_FQ"); + addr[nt] = &All.GlobalSigmaFq; + id[nt++] = FLOAT; +#endif + +#ifdef GLOBAL_SIGMA_CSFRD + strcpy(tag[nt], "Global_Sigma_CSFRD"); + addr[nt] = &All.GlobalSigmaCsfrd; + id[nt++] = FLOAT; +#endif + +#ifdef GLOBAL_SIGMA_SSFR + strcpy(tag[nt], "Global_Sigma_SSFR"); + addr[nt] = &All.GlobalSigmaSsfr; + id[nt++] = FLOAT; +#endif + +#ifdef GLOBAL_SIGMA_WP + strcpy(tag[nt], "Global_Sigma_WP"); + addr[nt] = &All.GlobalSigmaWp; + id[nt++] = FLOAT; +#endif + +#ifdef WRITE_MAINBRANCH + strcpy(tag[nt], "MainBranchMasses"); + addr[nt] = All.output_mass_mb; + id[nt++] = STRING; + + strcpy(tag[nt], "MainBranchBinSize"); + addr[nt] = All.output_mass_mb_bin; + id[nt++] = STRING; + + strcpy(tag[nt], "MainBranchMassType"); + addr[nt] = &All.MainBranchMassType; + id[nt++] = INT; + + strcpy(tag[nt], "MainBranchRedshift"); + addr[nt] = &All.mainBranchRedshift; + id[nt++] = FLOAT; +#endif + + if((fd = fopen(fname, "r"))) + { + while(!feof(fd)) + { + buf[0] = 0; + fgets(buf, 200, fd); + + if(sscanf(buf, "%s%s%s", buf1, buf2, buf3) < 2) + continue; + + if(buf1[0] == '%') + continue; + + for(i = 0, j = -1; i < nt; i++) + if(strcmp(buf1, tag[i]) == 0) + { + j = i; + tag[i][0] = 0; + break; + } + + if(j >= 0) + { + switch (id[j]) + { + case FLOAT: + *((double *) addr[j]) = atof(buf2); + break; + case STRING: + strcpy(addr[j], buf2); + break; + case INT: + *((int *) addr[j]) = atoi(buf2); + break; + } + printf("%s %-30s %s\n",All.startline,buf1,buf2); + } + else + { + fprintf(stdout, "%s Error in file %s: Tag '%s' not allowed or multiple defined.\n", All.startline, fname, buf1); + errorFlag = 1; + } + } + fclose(fd); + + } + else + { + fprintf(stdout, "%s Parameter file %s not found.\n", All.startline, fname); + errorFlag = 1; + } + + + for(i = 0; i < nt; i++) + { + if(*tag[i]) + { + fprintf(stdout, "%s Error. I miss a value for tag '%s' in parameter file '%s'.\n", All.startline, tag[i], fname); + errorFlag = 1; + } + } + + //Write name of output directory + sprintf(All.OutputDir,"output/%s",All.model_name); + + } + + //Communicate ErrorFlag + MPI_Bcast(&errorFlag, 1, MPI_INT, 0, MPI_COMM_WORLD); + + if(errorFlag) + { + MPI_Finalize(); + exit(0); + } + + //Communicate all global parameters to the other processes + MPI_Bcast(&All, sizeof(struct global_data), MPI_BYTE, 0, MPI_COMM_WORLD); + + //Make sure the number of tasks is a multiple of the number of universes + if(NTask % All.NUniverses != 0) + { + if(ThisTask == 0) printf("%s UniversesInParallel = %d, Number of processors = %d\n",All.startline,All.NUniverses,NTask); + endrun("Number of processors MUST be a multiple of number of UniversesInParallel"); + } + + //Make sure the number of walkers is a multiple of the number of universes + if(All.Nwalkers % (All.NUniverses*2) != 0 && (All.Mode==1 || All.Mode==11)) + { + if(ThisTask == 0) printf("%s UniversesInParallel = %d, Number of walkers = %d\n",All.startline,All.NUniverses,All.Nwalkers); + endrun("Number of walkers MUST be a multiple of twice the number of UniversesInParallel"); + } + if(All.Nwalkers % (All.NUniverses) != 0 && (All.Mode==2 || All.Mode==21 || All.Mode==3 || All.Mode==31)) + { + if(ThisTask == 0) printf("%s UniversesInParallel = %d, Number of walkers = %d\n",All.startline,All.NUniverses,All.Nwalkers); + endrun("Number of walkers MUST be a multiple of the number of UniversesInParallel"); + } + +#ifndef HDF5_SUPPORT + if (All.OutputFormat == 2) + { + All.OutputFormat = 1; + if (ThisTask == 0) printf("%s\n%s The output format cannot be set to 2 as HDF5 support is not enabled.\n",All.fullline,All.startline); + if (ThisTask == 0) printf("%s If HDF5 output files are required the code must be compiled with the option HDF5_SUPPORT.\n",All.startline); + if (ThisTask == 0) printf("%s Setting the output format to 1 (normal ascii output).\n",All.startline); + } +#endif + + if (All.OutputFormat < 1 || All.OutputFormat > 2) + { + All.OutputFormat = 1; + if (ThisTask == 0) printf("%s\n%s The output format is out of the defined range.\n",All.fullline,All.startline); + if (ThisTask == 0) printf("%s Setting the output format to 1 (normal ascii output).\n",All.startline); + } + +#undef FLOAT +#undef STRING +#undef INT +#undef MAXTAGS +} + + +/*! \brief This function initialises most global variables + * + * The number of tasks per universe are set and the Master task is stored. The memory manager is initialised. + * The starting time, the universal baryon fraction, the box length, the hubble time, and the minimum mass for + * the output are set. The starting parameters are set and the number of parameters is counted. All starting + * parameter values are copied to the #parameters structure #P. If not yet present, the output folder is created. + * The log files are opened and the random number generator or table is set. The stellar mass array for the + * interpolation #Mstar is computed. Finally, all global parameters that are the same on all tasks #All are + * broadcast from task 0. + */ + +void setup(void){ + + int i; + char buf[500], tmp1[NSTRING], *tmp2; + + //Print a line + if (ThisTask == 0) printf("%s\n",All.fullline); + + //Set the number of tasks per universe + All.NTaskPerUniverse = NTask / All.NUniverses; + + //Identify master task which is the main task for each universe + MasterTask = (ThisTask/All.NTaskPerUniverse) * All.NTaskPerUniverse; + + //Initialise the memory manager + malloc_init(); + + //Initialise the time + All.starttime = second(); + All.timenow = second() - All.starttime; + + //Define some variables + All.f_baryon = All.Omega_Baryon_0/All.Omega_0; + All.Lbox /= All.h_100; + All.Hubbletime = 9.7776E09/All.h_100; + + // Minimum mass for galaxy out put... + All.minmass = pow(10.,All.minmass)/All.m_unit; + + // Set number of parameters based on Initial Range (not a parameter to fit when Delta < 0) + All.Nparam = 0; + if (All.DeltaM0 >= 0.0) All.Nparam++; + if (All.DeltaEpsilon0 >= 0.0) All.Nparam++; + if (All.DeltaBeta0 >= 0.0) All.Nparam++; + if (All.DeltaGamma0 >= 0.0) All.Nparam++; + if (All.DeltaFesc >= 0.0) All.Nparam++; + if (All.DeltaFstrip >= 0.0) All.Nparam++; + if (All.DeltaTau0 >= 0.0) All.Nparam++; + +#ifdef SFE_MPEAK_ZEVOLV + if (All.DeltaMZ >= 0.0) All.Nparam++; +#else + All.MZ = 0.0; + All.DeltaMZ = -1.0; +#endif + +#ifdef SFE_NORM_ZEVOLV + if (All.DeltaEpsilonZ >= 0.0) All.Nparam++; +#else + All.EpsilonZ = 0.0; + All.DeltaEpsilonZ = -1.0; +#endif + +#ifdef SFE_BETA_ZEVOLV + if (All.DeltaBetaZ >= 0.0) All.Nparam++; +#else + All.BetaZ = 0.0; + All.DeltaBetaZ = -1.0; +#endif + +#ifdef SFE_GAMMA_ZEVOLV + if (All.DeltaGammaZ >= 0.0) All.Nparam++; +#else + All.GammaZ = 0.0; + All.DeltaGammaZ = -1.0; +#endif + +#ifdef SFE_SAME_SLOPE +#ifdef SFE_GAMMA_ZEVOLV + if (All.DeltaGamma0 >= 0.0) All.Nparam--; + if (All.DeltaGammaZ >= 0.0) All.Nparam--; + All.Gamma0 = All.Beta0; + All.GammaZ = All.BetaZ; + All.DeltaGamma0 = -1.0; + All.DeltaGammaZ = -1.0; +#else + if (All.DeltaGamma0 >= 0.0) All.Nparam--; + All.Gamma0 = All.Beta0; + All.DeltaGamma0 = -1.0; +#endif +#endif + +#ifdef SAT_SFR_EXP_DECAY + if (All.DeltaTauD >= 0.0) All.Nparam++; +#else + All.TauD = 0.0; + All.DeltaTauD = -1.0; +#endif + +#ifdef SAT_QUENCH_MASS_DEPENDENT + if (All.DeltaTauS >= 0.0) All.Nparam++; +#else + All.TauS = 0.0; + All.DeltaTauS = -1.0; +#endif + + //Initialise local parameters with value read from parameter file + P.M0 = All.M0; + P.Epsilon0 = All.Epsilon0; + P.Beta0 = All.Beta0; + P.Gamma0 = All.Gamma0; + P.MZ = All.MZ; + P.EpsilonZ = All.EpsilonZ; + P.BetaZ = All.BetaZ; + P.GammaZ = All.GammaZ; + P.Fesc = All.Fesc; + P.Fstrip = All.Fstrip; + P.Tau0 = All.Tau0; + P.TauS = All.TauS; + P.TauD = All.TauD; + + //Print number of free parameters + if (ThisTask == 0) printf("%s The total number of free parameters is %d\n",All.startline,All.Nparam); + + // Create Output directory + if (ThisTask == 0) + { + mkdir("output", 02755); + if (mkdir(All.OutputDir, 02755) == 0) + { + if (All.verbose >= VERBOSE_MIN) printf("%s Creating output directory '%s'\n",All.startline,All.OutputDir); + } + } + + //Open all log files + open_logfiles(); + + //Initialise random number generator or table + init_random_numbers(); + + //Initialise the number of observational statistics + All.Nobs = 0; + + //Initialise the number of time steps to zero + //If at some point it is larger we know the scale factors have been computed + All.NTimesteps = 0; + + //Create the stellar mass array + All.Nmstar = (int)((All.mstarmax - All.mstarmin) / All.dmstar) + 1; + //Check that the number of mass bins is not too low (at least 4 for the splines - though in practice much higher) + if (All.Nmstar < 4) + { + sprintf(buf, "The number of mass bins is %d which is lower than the minimim allowed number of 4.", All.Nmstar); + endrun(buf); + } + Mstar = emalloc("MStar", All.Nmstar * sizeof(float)); + for (i = 0; i < All.Nmstar; i++) Mstar[i] = (float)(All.mstarmin) + ((float)(i)+0.5) * (float)(All.dmstar); + + //Initialise the number of output reshifts + All.Noutputredshifts = i = 1; + //Parse the output redshift string for the first time + strcpy(tmp1,All.output_redshifts); + tmp2 = strtok(tmp1, ","); + //Parse the output redshift strings until all values have been parsed and increment the number of output redshifts + while ((tmp2 = strtok(NULL, ","))) All.Noutputredshifts++; + //Allocate the arrays that store the output redshifts and the corresponding snapshot numbers + OutputRedshifts = emalloc("OutputRedshifts", All.Noutputredshifts * sizeof(float)); + Output_iscale = emalloc("Output_iscale", All.Noutputredshifts * sizeof(int)); + //Parse the output redshift string for the first time (again) + strcpy(tmp1,All.output_redshifts); + tmp2 = strtok(tmp1, ","); + //Read the first output redshift + OutputRedshifts[0] = atof(tmp2); + if (OutputRedshifts[0] < 0.0) endrun("The output redshifts cannot be negative!"); + //Parse the output redshift strings until all values have been parsed and increment the index + while ((tmp2 = strtok(NULL, ","))) + { + OutputRedshifts[i] = atof(tmp2); + if (OutputRedshifts[i] < 0.0) endrun("The output redshifts cannot be negative!"); + i++; + } + +#ifdef WRITE_MAINBRANCH + //Initialise the number of masses for the main branch histories + All.Noutputbranch = i = 1; + //Parse the mass string for the first time + strcpy(tmp1,All.output_mass_mb); + tmp2 = strtok(tmp1, ","); + //Parse the mass string until all values have been parsed and increment the number of output masses + while ((tmp2 = strtok(NULL, ","))) All.Noutputbranch++; + //Allocate the arrays that store the output redshifts and the corresponding snapshot numbers + MainBranchMasses = emalloc("MainBranchMasses", All.Noutputbranch * sizeof(float)); + //Parse the mass string for the first time (again) + strcpy(tmp1,All.output_mass_mb); + tmp2 = strtok(tmp1, ","); + //Read the first mass + MainBranchMasses[0] = atof(tmp2); + if (MainBranchMasses[0] < 0.0) endrun("The main branch mass cannot be negative!"); + //Parse the mass string until all values have been parsed and increment the index + while ((tmp2 = strtok(NULL, ","))) + { + MainBranchMasses[i] = atof(tmp2); + if (MainBranchMasses[i] < 0.0) endrun("The main branch mass cannot be negative!"); + i++; + } + + //Initialise the number of mass bin sizes for the main branch histories + i = 1; + //Parse the mass string for the first time + strcpy(tmp1,All.output_mass_mb_bin); + tmp2 = strtok(tmp1, ","); + //Parse the mass string until all values have been parsed and increment the number of output masses + while ((tmp2 = strtok(NULL, ","))) i++; + //Check if the number of masses is the same as the number of bin sizes and abort if needed + if (i != All.Noutputbranch) endrun("The number of mass bin sizes needs to be equal to the number of masses for the main branch output!"); + //Reset i + i = 1; + //Allocate the arrays that store the output redshifts and the corresponding snapshot numbers + MainBranchBinSize = emalloc("MainBranchBinSize", All.Noutputbranch * sizeof(float)); + //Parse the mass string for the first time (again) + strcpy(tmp1,All.output_mass_mb_bin); + tmp2 = strtok(tmp1, ","); + //Read the first mass + MainBranchBinSize[0] = atof(tmp2); + if (MainBranchBinSize[0] < 0.0) endrun("The main branch mass bin size cannot be negative!"); + //Parse the mass string until all values have been parsed and increment the index + while ((tmp2 = strtok(NULL, ","))) + { + MainBranchBinSize[i] = atof(tmp2); + if (MainBranchBinSize[i] < 0.0) endrun("The main branch mass bin size cannot be negative!"); + i++; + } + + //If main branch mass type has a value that is not pre-defined set it to the default of 0, i.e. halo mass + if (All.MainBranchMassType < 0 && All.MainBranchMassType > 1) + { + All.MainBranchMassType = 0; + if (ThisTask == 0) printf("%s Main Branch Mass Type is out of bounds. Selecting default (halo mass).\n",All.startline); + } + + //If main branch redshift is negative, set it to redshift 0 + if (All.mainBranchRedshift < 0.0) + { + All.mainBranchRedshift = 0.0; + if (ThisTask == 0) printf("%s Main Branch Redshift cannot be negative. Setting it to z = 0.\n",All.startline); + } +#endif + + //Communicate all global parameters to the other processes + MPI_Bcast(&All, sizeof(struct global_data), MPI_BYTE, 0, MPI_COMM_WORLD); + +} + + +/*! \brief Frees all memory at the end of the run. + * + * This function frees all memory that is still allocated and closes the log files. + */ +void finalize (void) +{ + efree_movable(MassLeft); + efree_movable(DynTime); + efree_movable(Timestep); + efree_movable(CosmicTime); + efree_movable(ScaleFactor); + efree_movable(H); +#ifdef COMPUTE_ICM + efree_movable(NhalosInForest); + efree_movable(NtreesInForest); +#endif + efree_movable(OffsetHalos); + efree_movable(NhalosInTree); +#ifdef READ_WP + efree(Wp); + efree(WpSet); +#endif +#ifdef READ_SSFR + efree(Ssfr); + efree(SsfrSet); +#endif +#ifdef READ_CSFRD + efree(Csfrd); + efree(CsfrdSet); +#endif +#ifdef READ_FQ + efree(Fq); + efree(FqSet); +#endif +#ifdef READ_SMF + efree(Smf); + efree(SmfSet); +#endif +#ifdef WRITE_MAINBRANCH + efree(MainBranchBinSize); + efree(MainBranchMasses); +#endif + efree(Output_iscale); + efree(OutputRedshifts); + efree(Mstar); +#if (RANDOM_NUMBER_TABLE > 0) + efree(RndTableUniform); + efree(RndTableGaussian); +#endif + close_logfiles(); + gsl_rng_free(rng_gaussian); +} + + +/*! \brief Open files for logging. + * + * This function opens various log-files that report on the status and performance. Upon restart, the code + * will append to these files. + */ +void open_logfiles(void) +{ + char mode[2], buf[NSTRING], msg[NSTRING]; + + //if(RestartFlag == 0) + strcpy(mode, "w"); + //else + //strcpy(mode, "a"); + + if(ThisTask != 0) // only the root processors writes to the log files + return; + + sprintf(buf, "%s/%s", All.OutputDir, "memory.txt"); + if(!(FpMemory = fopen(buf, mode))) + { + sprintf(msg, "error in opening file '%s'\n", buf); + endrun(msg); + } + +} + + +/*! \brief Close the global log-files. + */ +void close_logfiles(void) +{ + if(ThisTask != 0) // only the root processors writes to the log files + return; + + fclose(FpMemory); + +} + + +/*! \brief This function initialises the random numbers. + * + * This function initialises the random numbers. If #RANDOM_NUMBER_TABLE is selected the random number + * table is allocated and filled with gaussian random numbers on each task + */ +void init_random_numbers(void) +{ +#if (RANDOM_NUMBER_TABLE > 0) + int i; +#endif + + //Initialise the generators and set the seed + rng_gaussian = gsl_rng_alloc(gsl_rng_ranlxs2); + rng_uniform = gsl_rng_alloc(gsl_rng_ranlxs2); + gsl_rng_set(rng_gaussian, 140314081082 + (long)(ThisTask % All.NTaskPerUniverse)); + gsl_rng_set(rng_uniform, 160982140314 + (long)(ThisTask % All.NTaskPerUniverse)); + + //If the random number table option is selected we store random numbers in a table +#if (RANDOM_NUMBER_TABLE > 0) + if (RANDOM_NUMBER_TABLE +#include +#include +#include +#include +#include +#include +#include +#include +#include "allvars.h" +#include "proto.h" + +#ifdef HDF5_SUPPORT +#include +#endif + + +/*! \brief This function adds a galaxy to all relevant statistics + * + * First the observed stellar mass and star formation rate is calculated. For this a gaussian + * scatter scaled for the current redshift is used. The scatter is either taken from the random + * number table, or if not available directly computed. Then the galaxy is added to the SMF, the + * SMF for quenched galaxies, the SSFRs, and the CSFRD. + * + * \param ihalo The index of the halo that is added to the statistics + * \param thisthread Number of the OpenMP thread that is responsible + */ +void add_galaxy_to_statistics(int ihalo, int thisthread) +{ + int imass, iscale; + float sigmaobs, mstarobs, sfrobs, scatter, logmass; + + // Add observational error to stellar masses + scatter = get_gaussian_random_number(ihalo); + sigmaobs = All.obssigma0+(1./H[ihalo].a-1.)*All.obssigmaz; + if (1./H[ihalo].a-1. > ZMAX_SMFERR) sigmaobs = All.obssigma0+(ZMAX_SMFERR)*All.obssigmaz; + mstarobs = H[ihalo].mstar * pow(10.,sigmaobs*scatter); + + // Add observational error to SFRs + scatter = get_gaussian_random_number(ihalo + RANDOM_NUMBER_TABLE/2); + sfrobs = H[ihalo].sfr * pow(10.,sigmaobs*scatter); + + if (mstarobs > 0) logmass = log10(mstarobs*All.m_unit); + else logmass = All.mstarmin - 1.0; + imass = (int)((logmass-All.mstarmin)/All.dmstar); + iscale = H[ihalo].iscale; + + if (logmass >= All.mstarmin && imass < All.Nmstar) + { + //Add galaxy to SMF + Modelsmf[iscale*All.Nmstar+imass+thisthread*All.NStatistics] + += 1.0/All.Lbox/All.Lbox/All.Lbox/All.dmstar; + //Add galaxy to quenched SMF + if (sfrobs <= 0.0) Modelsmfred[iscale*All.Nmstar+imass+thisthread*All.NStatistics] + += 1.0/All.Lbox/All.Lbox/All.Lbox/All.dmstar; + else if (log10(sfrobs)-logmass < log10(SSFRTHRESH/CosmicTime[iscale]/All.t_unit)) + Modelsmfred[iscale*All.Nmstar+imass+thisthread*All.NStatistics] += 1.0/All.Lbox/All.Lbox/All.Lbox/All.dmstar; + //Add galaxy to SSFR + Modelssfr[iscale*All.Nmstar+imass+thisthread*All.NStatistics] += sfrobs*All.m_unit/All.t_unit/mstarobs; + } + //Add galaxy to CSFRD + Modelcsfrd[iscale+thisthread*All.NTimesteps] += sfrobs * All.m_unit / All.t_unit / All.Lbox / All.Lbox / All.Lbox; + +} + + +/*! \brief This function computes the final statistics from all processors on each Master Task + * + * The stellar mass function in each slave task is copied to a local array smfComm, and then added + * to the master task's stellar mass function. The model stellar mass function for each observed + * data point is then interpolated from a 2D grid (mstar/a) using bicubic splines. The quenched + * fraction of galaxies is set as SMF/SMFred and interpolated as well. + */ +void get_statistics(void) +{ + int i,j,task; + float fquenched; + float *smfComm, *csfrdComm; + double *xa, *ya, *yb, *za; + const gsl_interp2d_type *T = gsl_interp2d_bicubic; + size_t nx,ny; + gsl_spline *spline1D; + gsl_spline2d *spline; + gsl_interp_accel *xacc, *yacc; + MPI_Status status; + + //Allocate the buffer arrays + smfComm = emalloc("SMFComm", All.NTimesteps * All.Nmstar * sizeof(float)); + csfrdComm = emalloc("CSFRDComm", All.NTimesteps * sizeof(float)); + + //If OpenMP is set we need to first collect all statistics using the sub-array for the first thread +#if defined(_OPENMP) && (OPENMPTHREADS > 1) + for (i = 1; i < NThread; i++) + { + for (j = 0; j < All.NStatistics; j++) + { + Modelsmf[j] += Modelsmf[j+i*All.NStatistics]; + Modelsmfred[j] += Modelsmfred[j+i*All.NStatistics]; + Modelssfr[j] += Modelssfr[j+i*All.NStatistics]; + } + for (j = 0; j < All.NTimesteps; j++) Modelcsfrd[j] += Modelcsfrd[j+i*All.NTimesteps]; + } +#endif + + //Go through each slave task + for (task = MasterTask + 1; task < MasterTask + All.NTaskPerUniverse; task++) + { + //if this is a slave task... + if (ThisTask == task) + { + //...send the model SMF to the master task + MPI_Ssend(Modelsmf, All.NTimesteps * All.Nmstar, MPI_FLOAT, MasterTask, TAG_SMF, MPI_COMM_WORLD); + MPI_Ssend(Modelsmfred, All.NTimesteps * All.Nmstar, MPI_FLOAT, MasterTask, TAG_SMFRED, MPI_COMM_WORLD); + MPI_Ssend(Modelcsfrd, All.NTimesteps, MPI_FLOAT, MasterTask, TAG_CSFRD, MPI_COMM_WORLD); + MPI_Ssend(Modelssfr, All.NTimesteps * All.Nmstar, MPI_FLOAT, MasterTask, TAG_SSFR, MPI_COMM_WORLD); + } + //if this is the master task... + if (ThisTask == MasterTask) + { + //...receive the SMF from the slave task, write it to the buffer and add it to the local SMF + MPI_Recv(smfComm, All.NTimesteps * All.Nmstar, MPI_FLOAT, task, TAG_SMF, MPI_COMM_WORLD, &status); + for (i = 0; i < All.NTimesteps * All.Nmstar; i++) Modelsmf[i] += smfComm[i]; + MPI_Recv(smfComm, All.NTimesteps * All.Nmstar, MPI_FLOAT, task, TAG_SMFRED, MPI_COMM_WORLD, &status); + for (i = 0; i < All.NTimesteps * All.Nmstar; i++) Modelsmfred[i] += smfComm[i]; + MPI_Recv(csfrdComm, All.NTimesteps, MPI_FLOAT, task, TAG_CSFRD, MPI_COMM_WORLD, &status); + for (i = 0; i < All.NTimesteps; i++) Modelcsfrd[i] += csfrdComm[i]; + MPI_Recv(smfComm, All.NTimesteps * All.Nmstar, MPI_FLOAT, task, TAG_SSFR, MPI_COMM_WORLD, &status); + for (i = 0; i < All.NTimesteps * All.Nmstar; i++) Modelssfr[i] += smfComm[i]; + } + } + //Free the buffers + efree(csfrdComm); + efree(smfComm); + + //The master task now does the interpolation + if (ThisTask == MasterTask) + { + //Define the number of 2D grid points for GSL + nx = (size_t)(All.NTimesteps); + ny = (size_t)(All.Nmstar); + //Allocate double arrays that will be passed to the splines + xa = emalloc("XA", nx * sizeof(double)); + ya = emalloc("YA", ny * sizeof(double)); + yb = emalloc("YB", nx * sizeof(double)); + za = emalloc("ZA", nx * ny * sizeof(double)); + //Allocate the spline and the acceleration arrays for GSL + spline = gsl_spline2d_alloc(T, nx, ny); + spline1D = gsl_spline_alloc(gsl_interp_cspline,nx); + xacc = gsl_interp_accel_alloc(); + yacc = gsl_interp_accel_alloc(); + //Write the scale factors and stellar masses to the arrays + for (i = 0; i < nx; i++) xa[i] = (double)(ScaleFactor[i]); + for (j = 0; j < ny; j++) ya[j] = (double)(Mstar[j]); + + //Now write the SMF to the za array + for (i = 0; i < nx; i++) + { + //For each grid point set the spline + for (j = 0; j < ny; j++) gsl_spline2d_set(spline, za, i, j, (double)(Modelsmf[i*ny+j])); + } + //Initialise the spline + gsl_spline2d_init(spline, xa, ya, za, nx, ny); +#ifdef READ_SMF + //Go through all observed data points + for (i = 0; i < All.Nsmf; i++) + { + //If the observed stellar mass is in range do a bicubic spline interpolation + if (Smf[i].obs_x > Mstar[0] && Smf[i].obs_x < Mstar[All.Nmstar-1] && Smf[i].bin > ScaleFactor[0] && Smf[i].bin <= ScaleFactor[All.NTimesteps-1]) + Smf[i].mod_y = gsl_spline2d_eval(spline, Smf[i].bin, Smf[i].obs_x, xacc, yacc); + //Otherwise set the model value to -1 + else Smf[i].mod_y = -1.0; + //And the poisson error + if (Smf[i].mod_y > 0) Smf[i].mod_sigma = 1.+1./sqrt(Smf[i].mod_y*All.Lbox*All.Lbox*All.Lbox*All.dmstar); + else Smf[i].mod_sigma = 1.0; + } +#endif +#ifdef READ_FQ + //Go through all observed data points + for (i = 0; i < All.Nfq; i++) + { + //If the observed stellar mass is in range do a bicubic spline interpolation + if (Fq[i].obs_x > Mstar[0] && Fq[i].obs_x < Mstar[All.Nmstar-1] && Fq[i].bin > ScaleFactor[0] && Fq[i].bin <= ScaleFactor[All.NTimesteps-1]) + Fq[i].mod_sigma = gsl_spline2d_eval(spline, Fq[i].bin, Fq[i].obs_x, xacc, yacc); + //Otherwise set the model value to -1 + else Fq[i].mod_sigma = -1.0; + if (Fq[i].mod_sigma > 0) Fq[i].mod_sigma = 1./sqrt(Fq[i].mod_sigma*All.Lbox*All.Lbox*All.Lbox*All.dmstar); + else Fq[i].mod_sigma = 1.0; + } +#endif +#ifdef READ_SSFR + //Go through all observed data points + for (i = 0; i < All.Nssfr; i++) + { + if (Ssfr[i].bin > Mstar[0] && Ssfr[i].bin < Mstar[All.Nmstar-1] && 1./(Ssfr[i].obs_x+1.0) > ScaleFactor[0] && 1./(Ssfr[i].obs_x+1.0) <= ScaleFactor[All.NTimesteps-1]) + Ssfr[i].mod_sigma = gsl_spline2d_eval(spline, 1./(Ssfr[i].obs_x+1.0), Ssfr[i].bin, xacc, yacc); + //Otherwise set the model value to -1 + else Ssfr[i].mod_sigma = -1.0; + if (Ssfr[i].mod_sigma > 0) Ssfr[i].mod_sigma = 1.+1./sqrt(Ssfr[i].mod_sigma*All.Lbox*All.Lbox*All.Lbox*All.dmstar); + else Ssfr[i].mod_sigma = 1.0; + } +#endif +#ifdef READ_FQ + //Now write the SMF for quenched galaxies to the za array + for (i = 0; i < nx; i++) + { + //For each grid point set the spline + for (j = 0; j < ny; j++) { + //Define the Quenched Fraction + if (Modelsmf[i*ny+j] > 0) fquenched = (double)(Modelsmfred[i*ny+j]) / (double)(Modelsmf[i*ny+j]); + else fquenched = 0.0; + gsl_spline2d_set(spline, za, i, j, fquenched); + } + } + //Initialise the spline + gsl_spline2d_init(spline, xa, ya, za, nx, ny); + //Go through all observed data points + for (i = 0; i < All.Nfq; i++) + { + //If the observed stellar mass is in range do a bicubic spline interpolation + if (Fq[i].obs_x > Mstar[0] && Fq[i].obs_x < Mstar[All.Nmstar-1] && Fq[i].bin > ScaleFactor[0] && Fq[i].bin <= ScaleFactor[All.NTimesteps-1]) + Fq[i].mod_y = gsl_spline2d_eval(spline, Fq[i].bin, Fq[i].obs_x, xacc, yacc); + //Otherwise set the model value to -1 + else Fq[i].mod_y = -1.0; + Fq[i].mod_sigma *= Fq[i].mod_y; + } +#endif +#ifdef READ_CSFRD + //Initialise the spline + for (i = 0; i < nx; i++) yb[i] = (double)(Modelcsfrd[i]); + gsl_spline_init(spline1D, xa, yb, nx); + //Go through all observed data points and do a bicubic spline interpolation + for (i = 0; i < All.Ncsfrd; i++) + { + if (1./(Csfrd[i].obs_x+1.0) > ScaleFactor[0] && 1./(Csfrd[i].obs_x+1.0) <= ScaleFactor[All.NTimesteps-1]) + Csfrd[i].mod_y = gsl_spline_eval(spline1D, 1./(Csfrd[i].obs_x+1.0), xacc); + else Csfrd[i].mod_y = -1.0; + } +#endif + //Normalise the SSFRs + for (i = 0; i < All.NTimesteps * All.Nmstar; i++) + { + if (Modelsmf[i] > 0) Modelssfr[i] /= (Modelsmf[i] * All.Lbox*All.Lbox*All.Lbox*All.dmstar); + else Modelssfr[i] = 0.0; + } +#ifdef READ_SSFR + //Now write the SMF to the za array + for (i = 0; i < nx; i++) + { + //For each grid point set the spline + for (j = 0; j < ny; j++) gsl_spline2d_set(spline, za, i, j, (double)(Modelssfr[i*ny+j])); + } + //Initialise the spline + gsl_spline2d_init(spline, xa, ya, za, nx, ny); + //Go through all observed data points + for (i = 0; i < All.Nssfr; i++) + { + //If the observed stellar mass is in range do a bicubic spline interpolation + if (Ssfr[i].bin > Mstar[0] && Ssfr[i].bin < Mstar[All.Nmstar-1] && 1./(Ssfr[i].obs_x+1.0) > ScaleFactor[0] && 1./(Ssfr[i].obs_x+1.0) <= ScaleFactor[All.NTimesteps-1]) + Ssfr[i].mod_y = gsl_spline2d_eval(spline, 1./(Ssfr[i].obs_x+1.0), Ssfr[i].bin, xacc, yacc); + //Otherwise set the model value to -1.0 + else Ssfr[i].mod_y = -1.0; + } +#endif + //Free all used arrays + efree(za); + efree(yb); + efree(ya); + efree(xa); + gsl_interp_accel_free(yacc); + gsl_interp_accel_free(xacc); + gsl_spline_free(spline1D); + gsl_spline2d_free(spline); + } + +#ifdef READ_WP + compute_wp(); +#endif + +} + + +/*! \brief This function prints all statistics to either ascii or hdf5 output files + * + */ +void write_statistics(void) +{ + if (All.OutputFormat == 2) +#ifdef HDF5_SUPPORT + write_statistics_init_hdf5(); +#else + write_statistics_init_ascii(); +#endif + else if (All.OutputFormat == 1) write_statistics_init_ascii(); + else write_statistics_init_ascii(); + +} + + +/*! \brief This function generates the folder for the statistiscs ascii files if needed and set the directory path + * + */ +void write_statistics_init_ascii(void) +{ + char outdir[NSTRING], outfname[NSTRING]; + FILE *ofp; + + //Get path + sprintf(outdir,"%s/statistics",All.OutputDir); + //Create directory if needed + if (ThisTask == 0) { + printf("%s\n%s Writing statistics to folder %s\n",All.fullline,All.startline,outdir); + if (mkdir(outdir, 02755) == 0) + { + if (All.verbose >= VERBOSE_MIN) printf("%s\n%s Creating statistics directory '%s'\n",All.fullline,All.startline,outdir); + } + } + //Wait for task 0 + MPI_Barrier(MPI_COMM_WORLD); + + //Task 0 creates the chi2 file + if (ThisTask == 0) + { //Create Chi2^2 file + sprintf(outfname,"%s/chi2.out",outdir); + ofp = fopen(outfname,"w"); + fclose(ofp); + } + + //Write all chi2 values to the output file + write_statistics_chi2(outdir,0); + //Write statistics files + write_statistics_ascii(outdir,(int)(MasterTask/All.NTaskPerUniverse)); + +} + + +#ifdef HDF5_SUPPORT +/*! \brief This function generates the HDF5 file for the statistiscs and set the file name + * + */ +void write_statistics_init_hdf5(void) +{ + char outfname[NSTRING]; + int itask; + hid_t stats_file; + herr_t status; + + //Set the file name + sprintf(outfname,"%s/statistics.h5",All.OutputDir); + + //Create file + if (ThisTask == 0) + { //Print what's done to the screen + printf("%s\n%s Writing statistics to file %s\n",All.fullline,All.startline,outfname); + //Create statistics file by opening and closing + stats_file = H5Fcreate(outfname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + status = H5Fclose(stats_file); + //Warn if there are problems + if (status != 0) printf("%s Task 0 failed to create file %s\n",All.startline,outfname); + } + + //Go through all tasks + for (itask = 0; itask < NTask; itask += All.NTaskPerUniverse) + { //If this is a master task write the statistics to a file + if (itask == ThisTask && ThisTask == MasterTask) + { //Each master task writes its own universe to the statistics file + write_statistics_hdf5(outfname, (int)(MasterTask/All.NTaskPerUniverse)); + } + //Block all tasks from continuing (so that universes are written sequentially + MPI_Barrier(MPI_COMM_WORLD); + }//End Master Task +} +#endif + + +/*! \brief This function prints all statistics to ascii files + * + */ +void write_statistics_ascii(char *outdir, int iuniverse) +{ + char outfname[NSTRING]; + int i,j; + FILE *ofp; + + //The master task now does the interpolation and output + if (ThisTask == MasterTask) + { + //Write the model SMF to a file for each master task + sprintf(outfname,"%s/smfmod.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + //The first column is the stellar mass followed by a SMF value for that mass at each scale factor + for (i = 0; i < All.Nmstar; i++) + { + fprintf(ofp,"%f",Mstar[i]); + for (j = 0; j < All.NTimesteps; j++) + fprintf(ofp," %e",Modelsmf[j*All.Nmstar+i]); + fprintf(ofp,"\n"); + } + fclose(ofp); + //Write the model quenched fractions to a file for each master task + sprintf(outfname,"%s/fqmod.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + //The first column is the stellar mass followed by a FQ value for that mass at each scale factor + for (i = 0; i < All.Nmstar; i++) + { + fprintf(ofp,"%f",Mstar[i]); + for (j = 0; j < All.NTimesteps; j++) + fprintf(ofp," %e",Modelsmfred[j*All.Nmstar+i]/Modelsmf[j*All.Nmstar+i]); + fprintf(ofp,"\n"); + } + fclose(ofp); + //Write the model cosmic SFR density to a file for each master task + sprintf(outfname,"%s/csfrdmod.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.NTimesteps; i++) fprintf(ofp,"%f %e\n",1.0/ScaleFactor[i]-1.0,Modelcsfrd[i]); + fclose(ofp); + //Write the model specific SFRs to a file for each master task + sprintf(outfname,"%s/ssfrmod.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + //The first column is the stellar mass followed by a SSFR value for that mass at each scale factor + for (i = 0; i < All.Nmstar; i++) + { + fprintf(ofp,"%f",Mstar[i]); + for (j = 0; j < All.NTimesteps; j++) + fprintf(ofp," %e",Modelssfr[j*All.Nmstar+i]); + fprintf(ofp,"\n"); + } + fclose(ofp); +#ifdef READ_SMF + //Now write the observed data points and the computed model SMF values to a file + sprintf(outfname,"%s/smfobs.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.Nsmfset; i++) + { + fprintf(ofp,"# %d %f %f %s\n",SmfSet[i].ndata,SmfSet[i].min,SmfSet[i].max,SmfSet[i].tag); + for (j = SmfSet[i].offset; j < SmfSet[i].offset + SmfSet[i].ndata; j++) + fprintf(ofp,"%f %f %f %f\n",Smf[j].obs_x,Smf[j].obs_y,Smf[j].obs_sigma,log10(Smf[j].mod_y)); + fprintf(ofp,"\n"); + } + fclose(ofp); +#endif +#ifdef READ_FQ + //Now write the observed data points and the computed model SMF values to a file + sprintf(outfname,"%s/fqobs.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.Nfqset; i++) + { + fprintf(ofp,"# %d %f %f %s\n",FqSet[i].ndata,FqSet[i].min,FqSet[i].max,FqSet[i].tag); + for (j = FqSet[i].offset; j < FqSet[i].offset + FqSet[i].ndata; j++) + fprintf(ofp,"%f %f %f %f\n",Fq[j].obs_x,Fq[j].obs_y,Fq[j].obs_sigma,Fq[j].mod_y); + fprintf(ofp,"\n"); + } + fclose(ofp); +#endif +#ifdef READ_CSFRD + //Now write the observed data points and the computed model CSFRD values to a file + sprintf(outfname,"%s/csfrdobs.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.Ncsfrdset; i++) + { + fprintf(ofp,"# %d %s\n",CsfrdSet[i].ndata,CsfrdSet[i].tag); + for (j = CsfrdSet[i].offset; j < CsfrdSet[i].offset + CsfrdSet[i].ndata; j++) + fprintf(ofp,"%f %f %f %f\n",Csfrd[j].obs_x,Csfrd[j].obs_y,Csfrd[j].obs_sigma,log10(Csfrd[j].mod_y*All.m_unit/All.t_unit/pow(All.x_unit,3.0))); + fprintf(ofp,"\n"); + } + fclose(ofp); +#endif +#ifdef READ_SSFR + //Now write the observed data points and the computed model SSFR values to a file + sprintf(outfname,"%s/ssfrobs.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.Nssfrset; i++) + { + fprintf(ofp,"# %d %s\n",SsfrSet[i].ndata,SsfrSet[i].tag); + for (j = SsfrSet[i].offset; j < SsfrSet[i].offset + SsfrSet[i].ndata; j++) + fprintf(ofp,"%f %f %f %f %f\n",Ssfr[j].obs_x,Ssfr[j].bin,Ssfr[j].obs_y,Ssfr[j].obs_sigma,log10(Ssfr[j].mod_y/All.t_unit)); + fprintf(ofp,"\n"); + } + fclose(ofp); +#endif +#ifdef READ_WP + //Write the model 3d correlation functions to a file for each master task + sprintf(outfname,"%s/ximod.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + //The three columns are the radius, xi and xi's error - repeated for each stellar mass bin + fprintf(ofp,"#"); + for (j = 0; j < All.Nwpset; j++) fprintf(ofp," [%f %f]",WpSet[j].min,WpSet[j].max); + fprintf(ofp,"\n"); + for (i = 0; i < WP_RBINS_INT; i++) + { + for (j = 0; j < All.Nwpset; j++) fprintf(ofp,"%e %e %e ",Radius[WP_RBINS_INT * j + i], Modelxi[WP_RBINS_INT * j + i], Modelxierr[WP_RBINS_INT * j + i]); + fprintf(ofp,"\n"); + } + fclose(ofp); + //Now write the observed data points and the computed model wp values to a file + sprintf(outfname,"%s/wpobs.%d.out",outdir,iuniverse); + ofp = fopen(outfname,"w"); + for (i = 0; i < All.Nwpset; i++) + { + fprintf(ofp,"# %d %f %f %f %s\n",WpSet[i].ndata,WpSet[i].min,WpSet[i].max,WpSet[i].cut,WpSet[i].tag); + for (j = WpSet[i].offset; j < WpSet[i].ndata + WpSet[i].offset; j++) + fprintf(ofp,"%e %e %e %e %e\n",Wp[j].obs_x,Wp[j].obs_y,Wp[j].obs_sigma,Wp[j].mod_y,Wp[j].mod_sigma); + fprintf(ofp,"\n"); + } + fclose(ofp); +#endif + } + +} + + +#ifdef HDF5_SUPPORT +/*! \brief This function prints all statistics of one universe to a HDF5 file + * + */ +void write_statistics_hdf5(char *outfname, int iuniverse) +{ + int i, j; + float *data, *chi2;; + char path[NSTRING], buffer[SSTRING]; + hid_t stats_file, universe_group, set_group, data_group, dspace, dset, dtype, ftype, strtype; + hsize_t dims2[2], dims1[1]; + herr_t status; + + struct comp_smfset {int i1, i2; float f1, f2; char tag[SSTRING];} *compsmfset; + struct comp_fqset {int i1, i2; float f1, f2; char tag[SSTRING];} *compfqset; + struct comp_csfrdset {int i1, i2; char tag[SSTRING];} *compcsfrdset; + struct comp_ssfrset {int i1, i2; char tag[SSTRING];} *compssfrset; + struct comp_wpset {int i1, i2; float f1, f2, f3; char tag[SSTRING];} *compwpset; + struct comp_smf {float f1, f2, f3, f4, f5, f6;} *compsmf; + struct comp_fq {float f1, f2, f3, f4, f5, f6;} *compfq; + struct comp_csfrd {float f1, f2, f3, f4;} *compcsfrd; + struct comp_ssfr {float f1, f2, f3, f4, f5, f6;} *compssfr; + struct comp_xi {float f1, f2, f3;} *compxi; + struct comp_wp {float f1, f2, f3, f4, f5;} *compwp; + + //If this is the first master task open a new file + stats_file = H5Fopen(outfname, H5F_ACC_RDWR, H5P_DEFAULT); + + //Set the path name for this universe + sprintf(path,"/Universe_%06d",iuniverse); + //Create new group for this universe + universe_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Stellar mass functions // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //Set the path name for the SMFs + sprintf(path,"/Universe_%06d/SMF",iuniverse); + //Create new group for the SMFs + set_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + //First we write the model SMFs + //Allocate data array + data = emalloc("DATA", (All.NTimesteps+1) * (All.Nmstar+1) * sizeof(float)); + //Fill data array + data[0] = 0.0; + for (i = 0; i < All.NTimesteps; i++) data[i+1] = ScaleFactor[i]; + for (i = 0; i < All.Nmstar; i++) + { + data[(All.NTimesteps+1)*(i+1)] = Mstar[i]; + for (j = 0; j < All.NTimesteps; j++) data[(All.NTimesteps+1)*(i+1)+j+1] = log10(Modelsmf[j*All.Nmstar+i]); + } + //Set dimensions array + dims2[0] = All.Nmstar + 1; + dims2[1] = All.NTimesteps + 1; + //Set the path name for the model SMFs + sprintf(path,"/Universe_%06d/SMF/Model",iuniverse); + //Create dataspace and dataset, and write the dataset + dspace = H5Screate_simple(2, dims2, NULL); + dset = H5Dcreate(stats_file, path, H5T_NATIVE_FLOAT, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free data array + efree(data); + +#ifdef READ_SMF + //The next block is the information on the data sets + //Allocate compound + compsmfset = emalloc("COMPOUND_SMF", All.Nsmfset * sizeof(struct comp_smfset)); + //Fill compound + for (i = 0; i < All.Nsmfset; i++) + { + compsmfset[i].i1 = SmfSet[i].ndata; + compsmfset[i].i2 = SmfSet[i].offset; + compsmfset[i].f1 = SmfSet[i].min; + compsmfset[i].f2 = SmfSet[i].max; + strcpy(compsmfset[i].tag,SmfSet[i].tag); + } + //Create variable-length string datatype. + strtype= H5Tcopy(H5T_C_S1); + status = H5Tset_size(strtype, SSTRING); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_smfset)); + status = H5Tinsert(dtype, "Ndata", HOFFSET(struct comp_smfset, i1), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Offset", HOFFSET(struct comp_smfset, i2), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Redshift_min", HOFFSET(struct comp_smfset, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Redshift_max", HOFFSET(struct comp_smfset, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Tag", HOFFSET(struct comp_smfset, tag), strtype); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + SSTRING); + status = H5Tinsert(ftype, "Ndata", 0, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Offset", 4, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Redshift_min", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Redshift_max", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Tag", 4+4+4+4, strtype); + //Set dimensions array + dims1[0] = All.Nsmfset; + //Set the path name for the model SMFs + sprintf(path,"/Universe_%06d/SMF/Sets",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compsmfset); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + // Close the string type + status = H5Tclose(strtype); + //Free compound + efree(compsmfset); + + //Last entry are all observed SMFs and the corresponding model values + //Set the path name for the SMFs + sprintf(path,"/Universe_%06d/SMF/Data",iuniverse); + //Create new group for the SMF data + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_smf)); + status = H5Tinsert(dtype, "Stellar_mass", HOFFSET(struct comp_smf, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Phi_observed", HOFFSET(struct comp_smf, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_observed", HOFFSET(struct comp_smf, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Mean_ScaleFactor", HOFFSET(struct comp_smf, f4), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Phi_model", HOFFSET(struct comp_smf, f5), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_model", HOFFSET(struct comp_smf, f6), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + 4 + 4); + status = H5Tinsert(ftype, "Stellar_mass", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Phi_observed", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_observed", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Mean_ScaleFactor", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Phi_model", 4+4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_model", 4+4+4+4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Nsmfset; i++) + { //Allocate compound + compsmf = emalloc("COMPOUND_SMF", SmfSet[i].ndata * sizeof(struct comp_smf)); + //Fill compound + for (j = 0; j < SmfSet[i].ndata; j++) + { + compsmf[j].f1 = Smf[SmfSet[i].offset + j].obs_x; + compsmf[j].f2 = Smf[SmfSet[i].offset + j].obs_y; + compsmf[j].f3 = Smf[SmfSet[i].offset + j].obs_sigma; + compsmf[j].f4 = Smf[SmfSet[i].offset + j].bin; + compsmf[j].f5 = log10(Smf[SmfSet[i].offset + j].mod_y); + compsmf[j].f6 = log10(Smf[SmfSet[i].offset + j].mod_sigma); + } + //Set dimensions array + dims1[0] = SmfSet[i].ndata; + //Write tag to buffer and remove slashes so that the path is defined correctly + strcpy(buffer,SmfSet[i].tag); + for (j = 0; j < SSTRING; j++) if (buffer[j]=='/') buffer[j] = ' '; + //Set the path name for the model SMFs + sprintf(path,"/Universe_%06d/SMF/Data/%03d %s (z = %.2lf - %.2lf)",iuniverse,i,buffer,SmfSet[i].min,SmfSet[i].max); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compsmf); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compsmf); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); +#endif + //Close SMFs group + status = H5Gclose(set_group); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Quenched Fractions // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //Set the path name for the FQs + sprintf(path,"/Universe_%06d/FQ",iuniverse); + //Create new group for the FQs + set_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + //First we write the model FQs + //Allocate data array + data = emalloc("DATA", (All.NTimesteps+1) * (All.Nmstar+1) * sizeof(float)); + //Fill data array + data[0] = 0.0; + for (i = 0; i < All.NTimesteps; i++) data[i+1] = ScaleFactor[i]; + for (i = 0; i < All.Nmstar; i++) + { + data[(All.NTimesteps+1)*(i+1)] = Mstar[i]; + for (j = 0; j < All.NTimesteps; j++) data[(All.NTimesteps+1)*(i+1)+j+1] = Modelsmfred[j*All.Nmstar+i]/Modelsmf[j*All.Nmstar+i]; + } + //Set dimensions array + dims2[0] = All.Nmstar + 1; + dims2[1] = All.NTimesteps + 1; + //Set the path name for the model FQs + sprintf(path,"/Universe_%06d/FQ/Model",iuniverse); + //Create dataspace and dataset, and write the dataset + dspace = H5Screate_simple(2, dims2, NULL); + dset = H5Dcreate(stats_file, path, H5T_NATIVE_FLOAT, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free data array + efree(data); + +#ifdef READ_FQ + //The next block is the information on the data sets + //Allocate compound + compfqset = emalloc("COMPOUND_FQ", All.Nfqset * sizeof(struct comp_fqset)); + //Fill compound + for (i = 0; i < All.Nfqset; i++) + { + compfqset[i].i1 = FqSet[i].ndata; + compfqset[i].i2 = FqSet[i].offset; + compfqset[i].f1 = FqSet[i].min; + compfqset[i].f2 = FqSet[i].max; + strcpy(compfqset[i].tag,FqSet[i].tag); + } + //Create variable-length string datatype. + strtype= H5Tcopy(H5T_C_S1); + status = H5Tset_size(strtype, SSTRING); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_fqset)); + status = H5Tinsert(dtype, "Ndata", HOFFSET(struct comp_fqset, i1), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Offset", HOFFSET(struct comp_fqset, i2), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Redshift_min", HOFFSET(struct comp_fqset, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Redshift_max", HOFFSET(struct comp_fqset, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Tag", HOFFSET(struct comp_fqset, tag), strtype); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + SSTRING); + status = H5Tinsert(ftype, "Ndata", 0, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Offset", 4, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Redshift_min", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Redshift_max", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Tag", 4+4+4+4, strtype); + //Set dimensions array + dims1[0] = All.Nfqset; + //Set the path name for the model FQs + sprintf(path,"/Universe_%06d/FQ/Sets",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compfqset); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + // Close the string type + status = H5Tclose(strtype); + //Free compound + efree(compfqset); + + //Last entry are all observed FQs and the corresponding model values + //Set the path name for the FQs + sprintf(path,"/Universe_%06d/FQ/Data",iuniverse); + //Create new group for the FQ data + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_fq)); + status = H5Tinsert(dtype, "Stellar_mass", HOFFSET(struct comp_fq, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Fq_observed", HOFFSET(struct comp_fq, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_observed", HOFFSET(struct comp_fq, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Mean_ScaleFactor", HOFFSET(struct comp_fq, f4), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Fq_model", HOFFSET(struct comp_fq, f5), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_model", HOFFSET(struct comp_fq, f6), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + 4 + 4); + status = H5Tinsert(ftype, "Stellar_mass", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Fq_observed", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_observed", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Mean_ScaleFactor", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Fq_model", 4+4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_model", 4+4+4+4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Nfqset; i++) + { //Allocate compound + compfq = emalloc("COMPOUND_FQ", FqSet[i].ndata * sizeof(struct comp_fq)); + //Fill compound + for (j = 0; j < FqSet[i].ndata; j++) + { + compfq[j].f1 = Fq[FqSet[i].offset + j].obs_x; + compfq[j].f2 = Fq[FqSet[i].offset + j].obs_y; + compfq[j].f3 = Fq[FqSet[i].offset + j].obs_sigma; + compfq[j].f4 = Fq[FqSet[i].offset + j].bin; + compfq[j].f5 = Fq[FqSet[i].offset + j].mod_y; + compfq[j].f6 = Fq[FqSet[i].offset + j].mod_sigma; + } + //Set dimensions array + dims1[0] = FqSet[i].ndata; + //Write tag to buffer and remove slashes so that the path is defined correctly + strcpy(buffer,FqSet[i].tag); + for (j = 0; j < SSTRING; j++) if (buffer[j]=='/') buffer[j] = ' '; + //Set the path name for the model FQs + sprintf(path,"/Universe_%06d/FQ/Data/%03d %s (z = %.2lf - %.2lf)",iuniverse,i,buffer,FqSet[i].min,FqSet[i].max); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compfq); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compfq); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); +#endif + //Close FQs group + status = H5Gclose(set_group); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Specific star formation rate density // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //Set the path name for the CSFRD + sprintf(path,"/Universe_%06d/CSFRD",iuniverse); + //Create new group for the CSFRD + set_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + //First we write the model CSFRD + //Allocate data array + data = emalloc("DATA", All.NTimesteps * 2 * sizeof(float)); + //Fill data array + for (i = 0; i < All.NTimesteps; i++) + { + data[i*2] = 1.0 / ScaleFactor[i] - 1.0; + data[i*2+1] = log10(Modelcsfrd[i]); + } + //Set dimensions array + dims2[0] = All.NTimesteps; + dims2[1] = 2; + //Set the path name for the model CSFRD + sprintf(path,"/Universe_%06d/CSFRD/Model",iuniverse); + //Create dataspace and dataset, and write the dataset + dspace = H5Screate_simple(2, dims2, NULL); + dset = H5Dcreate(stats_file, path, H5T_NATIVE_FLOAT, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free data array + efree(data); + +#ifdef READ_CSFRD + //The next block is the information on the data sets + //Allocate compound + compcsfrdset = emalloc("COMPOUND_CSFRD", All.Ncsfrdset * sizeof(struct comp_csfrdset)); + //Fill compound + for (i = 0; i < All.Ncsfrdset; i++) + { + compcsfrdset[i].i1 = CsfrdSet[i].ndata; + compcsfrdset[i].i2 = CsfrdSet[i].offset; + strcpy(compcsfrdset[i].tag,CsfrdSet[i].tag); + } + //Create variable-length string datatype. + strtype= H5Tcopy(H5T_C_S1); + status = H5Tset_size(strtype, SSTRING); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_csfrdset)); + status = H5Tinsert(dtype, "Ndata", HOFFSET(struct comp_csfrdset, i1), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Offset", HOFFSET(struct comp_csfrdset, i2), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Tag", HOFFSET(struct comp_csfrdset, tag), strtype); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + SSTRING); + status = H5Tinsert(ftype, "Ndata", 0, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Offset", 4, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Tag", 4+4, strtype); + //Set dimensions array + dims1[0] = All.Ncsfrdset; + //Set the path name for the model FQs + sprintf(path,"/Universe_%06d/CSFRD/Sets",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compfqset); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + // Close the string type + status = H5Tclose(strtype); + //Free compound + efree(compcsfrdset); + + //Last entry are all observed CSFRDs and the corresponding model values + //Set the path name for the CSFRD + sprintf(path,"/Universe_%06d/CSFRD/Data",iuniverse); + //Create new group for the CSFRD data + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_csfrd)); + status = H5Tinsert(dtype, "Redshift", HOFFSET(struct comp_csfrd, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Csfrd_observed", HOFFSET(struct comp_csfrd, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_observed", HOFFSET(struct comp_csfrd, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Csfrd_model", HOFFSET(struct comp_csfrd, f4), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4); + status = H5Tinsert(ftype, "Redshift", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Csfrd_observed", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_observed", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Csfrd_model", 4+4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Ncsfrdset; i++) + { //Allocate compound + compcsfrd = emalloc("COMPOUND_CSFRD", CsfrdSet[i].ndata * sizeof(struct comp_csfrd)); + //Fill compound + for (j = 0; j < CsfrdSet[i].ndata; j++) + { + compcsfrd[j].f1 = Csfrd[CsfrdSet[i].offset + j].obs_x; + compcsfrd[j].f2 = Csfrd[CsfrdSet[i].offset + j].obs_y; + compcsfrd[j].f3 = Csfrd[CsfrdSet[i].offset + j].obs_sigma; + compcsfrd[j].f4 = log10(Csfrd[CsfrdSet[i].offset + j].mod_y * All.m_unit / All.t_unit * pow(All.x_unit,-3.0)); + } + //Set dimensions array + dims1[0] = CsfrdSet[i].ndata; + //Write tag to buffer and remove slashes so that the path is defined correctly + strcpy(buffer,CsfrdSet[i].tag); + for (j = 0; j < SSTRING; j++) if (buffer[j]=='/') buffer[j] = ' '; + //Set the path name for the model CSFRD + sprintf(path,"/Universe_%06d/CSFRD/Data/%03d %s",iuniverse,i,buffer); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compcsfrd); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compcsfrd); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); + +#endif + //Close SFRDS group + status = H5Gclose(set_group); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Specific Star Formation Rates // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //Set the path name for the SSFRs + sprintf(path,"/Universe_%06d/SSFR",iuniverse); + //Create new group for the SSFRs + set_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + //First we write the model SSFRs + //Allocate data array + data = emalloc("DATA", (All.NTimesteps+1) * (All.Nmstar+1) * sizeof(float)); + //Fill data array + data[0] = 0.0; + for (i = 0; i < All.Nmstar; i++) data[i+1] = Mstar[i]; + for (i = 0; i < All.NTimesteps; i++) + { + data[(All.Nmstar+1)*(i+1)] = 1.0 / ScaleFactor[i] - 1.0; + for (j = 0; j < All.Nmstar; j++) data[(All.Nmstar+1)*(i+1)+j+1] = log10(Modelssfr[i*All.Nmstar+j]/All.t_unit); + } + //Set dimensions array + dims2[0] = All.NTimesteps + 1; + dims2[1] = All.Nmstar + 1; + //Set the path name for the model SSFRs + sprintf(path,"/Universe_%06d/SSFR/Model",iuniverse); + //Create dataspace and dataset, and write the dataset + dspace = H5Screate_simple(2, dims2, NULL); + dset = H5Dcreate(stats_file, path, H5T_NATIVE_FLOAT, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, data); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free data array + efree(data); + +#ifdef READ_SSFR + //The next block is the information on the data sets + //Allocate compound + compssfrset = emalloc("COMPOUND_SSFR", All.Nssfrset * sizeof(struct comp_ssfrset)); + //Fill compound + for (i = 0; i < All.Nssfrset; i++) + { + compssfrset[i].i1 = SsfrSet[i].ndata; + compssfrset[i].i2 = SsfrSet[i].offset; + strcpy(compssfrset[i].tag,SsfrSet[i].tag); + } + //Create variable-length string datatype. + strtype= H5Tcopy(H5T_C_S1); + status = H5Tset_size(strtype, SSTRING); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_ssfrset)); + status = H5Tinsert(dtype, "Ndata", HOFFSET(struct comp_ssfrset, i1), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Offset", HOFFSET(struct comp_ssfrset, i2), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Tag", HOFFSET(struct comp_ssfrset, tag), strtype); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + SSTRING); + status = H5Tinsert(ftype, "Ndata", 0, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Offset", 4, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Tag", 4+4, strtype); + //Set dimensions array + dims1[0] = All.Nssfrset; + //Set the path name for the model SSFRs + sprintf(path,"/Universe_%06d/SSFR/Sets",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compssfrset); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + // Close the string type + status = H5Tclose(strtype); + //Free compound + efree(compssfrset); + + //Last entry are all observed SSFRs and the corresponding model values + //Set the path name for the SSFRs + sprintf(path,"/Universe_%06d/SSFR/Data",iuniverse); + //Create new group for the SSFR data + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_ssfr)); + status = H5Tinsert(dtype, "Redshift", HOFFSET(struct comp_ssfr, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Ssfr_observed", HOFFSET(struct comp_ssfr, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_observed", HOFFSET(struct comp_ssfr, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Stellar_mass", HOFFSET(struct comp_ssfr, f4), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Ssfr_model", HOFFSET(struct comp_ssfr, f5), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_model", HOFFSET(struct comp_ssfr, f6), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + 4 + 4); + status = H5Tinsert(ftype, "Redshift", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Ssfr_observed", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_observed", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Stellar_mass", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Ssfr_model", 4+4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_model", 4+4+4+4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Nssfrset; i++) + { //Allocate compound + compssfr = emalloc("COMPOUND_SSFR", SsfrSet[i].ndata * sizeof(struct comp_ssfr)); + //Fill compound + for (j = 0; j < SsfrSet[i].ndata; j++) + { + compssfr[j].f1 = Ssfr[SsfrSet[i].offset + j].obs_x; + compssfr[j].f2 = Ssfr[SsfrSet[i].offset + j].obs_y; + compssfr[j].f3 = Ssfr[SsfrSet[i].offset + j].obs_sigma; + compssfr[j].f4 = Ssfr[SsfrSet[i].offset + j].bin; + compssfr[j].f5 = log10(Ssfr[SsfrSet[i].offset + j].mod_y / All.t_unit); + compssfr[j].f6 = log10(Ssfr[SsfrSet[i].offset + j].mod_sigma); + } + //Set dimensions array + dims1[0] = SsfrSet[i].ndata; + //Write tag to buffer and remove slashes so that the path is defined correctly + strcpy(buffer,SsfrSet[i].tag); + for (j = 0; j < SSTRING; j++) if (buffer[j]=='/') buffer[j] = ' '; + //Set the path name for the model FQs + sprintf(path,"/Universe_%06d/SSFR/Data/%03d %s",iuniverse,i,buffer); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compssfr); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compssfr); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); +#endif + //Close SSFRs group + status = H5Gclose(set_group); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Clustering // + ////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef READ_WP + //Set the path name for the clustering + sprintf(path,"/Universe_%06d/Clustering",iuniverse); + //Create new group for the clustering + set_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + + //First entry are all 3D model correlation functions + //Set the path name for the 3D correlation functions + sprintf(path,"/Universe_%06d/Clustering/Model_3D",iuniverse); + //Create new group for the 3D correlation functions + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_xi)); + status = H5Tinsert(dtype, "Radius", HOFFSET(struct comp_xi, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Xi", HOFFSET(struct comp_xi, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_xi", HOFFSET(struct comp_xi, f3), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4); + status = H5Tinsert(ftype, "Radius", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Xi", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_xi", 4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Nwpset; i++) + { //Allocate compound + compxi = emalloc("COMPOUND_XI", WP_RBINS_INT * sizeof(struct comp_xi)); + //Fill compound + for (j = 0; j < WP_RBINS_INT; j++) + { + compxi[j].f1 = Radius[WP_RBINS_INT * i + j]; + compxi[j].f2 = Modelxi[WP_RBINS_INT * i + j]; + compxi[j].f3 = Modelxierr[WP_RBINS_INT * i + j]; + } + //Set dimensions array + dims1[0] = WP_RBINS_INT; + //Set the path name for the model correlation function + sprintf(path,"/Universe_%06d/Clustering/Model_3D/%03d - m = %.2f-%.2f - z = %.2f",iuniverse,i,log10(WpSet[i].min*All.m_unit),log10(WpSet[i].max*All.m_unit),All.wpredshift); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compxi); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compxi); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); + + //The next block is the information on the data sets + //Allocate compound + compwpset = emalloc("COMPOUND_WP", All.Nwpset * sizeof(struct comp_wpset)); + //Fill compound + for (i = 0; i < All.Nwpset; i++) + { + compwpset[i].i1 = WpSet[i].ndata; + compwpset[i].i2 = WpSet[i].offset; + compwpset[i].f1 = log10(WpSet[i].min*All.m_unit); + compwpset[i].f2 = log10(WpSet[i].max*All.m_unit); + compwpset[i].f3 = WpSet[i].cut; + strcpy(compwpset[i].tag,WpSet[i].tag); + } + //Create variable-length string datatype. + strtype= H5Tcopy(H5T_C_S1); + status = H5Tset_size(strtype, SSTRING); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_wpset)); + status = H5Tinsert(dtype, "Ndata", HOFFSET(struct comp_wpset, i1), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Offset", HOFFSET(struct comp_wpset, i2), H5T_NATIVE_INT); + status = H5Tinsert(dtype, "Minimum_Mass", HOFFSET(struct comp_wpset, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Maximum_Mass", HOFFSET(struct comp_wpset, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Pi_max", HOFFSET(struct comp_wpset, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Tag", HOFFSET(struct comp_wpset, tag), strtype); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + 4 + SSTRING); + status = H5Tinsert(ftype, "Ndata", 0, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Offset", 4, H5T_STD_I32LE); + status = H5Tinsert(ftype, "Minimum_Mass", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Maximum_Mass", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Pi_max", 4+4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Tag", 4+4+4+4+4, strtype); + //Set dimensions array + dims1[0] = All.Nwpset; + //Set the path name for the model projected correlation functions + sprintf(path,"/Universe_%06d/Clustering/Sets",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compwpset); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + // Close the string type + status = H5Tclose(strtype); + //Free compound + efree(compwpset); + + //Last entry are all observed projected correlation functions and the corresponding model values + //Set the path name for the WPs + sprintf(path,"/Universe_%06d/Clustering/Data",iuniverse); + //Create new group for the WP data + data_group = H5Gcreate(stats_file, path, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct comp_wp)); + status = H5Tinsert(dtype, "Radius", HOFFSET(struct comp_wp, f1), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Wp_observed", HOFFSET(struct comp_wp, f2), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_observed", HOFFSET(struct comp_wp, f3), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Wp_model", HOFFSET(struct comp_wp, f4), H5T_NATIVE_FLOAT); + status = H5Tinsert(dtype, "Sigma_model", HOFFSET(struct comp_wp, f5), H5T_NATIVE_FLOAT); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 4 + 4 + 4 + 4 + 4); + status = H5Tinsert(ftype, "Radius", 0, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Wp_observed", 4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_observed", 4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Wp_model", 4+4+4, H5T_IEEE_F32LE); + status = H5Tinsert(ftype, "Sigma_model", 4+4+4+4, H5T_IEEE_F32LE); + //Go through all data sets + for (i = 0; i < All.Nwpset; i++) + { //Allocate compound + compwp = emalloc("COMPOUND_WP", WpSet[i].ndata * sizeof(struct comp_wp)); + //Fill compound + for (j = 0; j < WpSet[i].ndata; j++) + { + compwp[j].f1 = Wp[WpSet[i].offset + j].obs_x; + compwp[j].f2 = Wp[WpSet[i].offset + j].obs_y; + compwp[j].f3 = Wp[WpSet[i].offset + j].obs_sigma; + compwp[j].f4 = Wp[WpSet[i].offset + j].mod_y; + compwp[j].f5 = Wp[WpSet[i].offset + j].mod_sigma; + } + //Set dimensions array + dims1[0] = WpSet[i].ndata; + //Write tag to buffer and remove slashes so that the path is defined correctly + strcpy(buffer,WpSet[i].tag); + for (j = 0; j < SSTRING; j++) if (buffer[j]=='/') buffer[j] = ' '; + //Set the path name for the model WPs + sprintf(path,"/Universe_%06d/Clustering/Data/%03d %s - m = %.2f-%.2f - z = %.2f",iuniverse,i,buffer,log10(WpSet[i].min*All.m_unit),log10(WpSet[i].max*All.m_unit),All.wpredshift); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, compwp); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free compound + efree(compwp); + } + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + //Close data group + status = H5Gclose(data_group); + + //Close clustering group + status = H5Gclose(set_group); +#endif + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Model parameters // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + //Specify data type + dtype = H5Tcreate(H5T_COMPOUND, sizeof(struct parameters)); + status = H5Tinsert(dtype, "M0", HOFFSET(struct parameters, M0), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Epsilon0", HOFFSET(struct parameters, Epsilon0), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Beta0", HOFFSET(struct parameters, Beta0), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Gamma0", HOFFSET(struct parameters, Gamma0), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "MZ", HOFFSET(struct parameters, MZ), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "EpsilonZ", HOFFSET(struct parameters, EpsilonZ), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "BetaZ", HOFFSET(struct parameters, BetaZ), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "GammaZ", HOFFSET(struct parameters, GammaZ), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Fesc", HOFFSET(struct parameters, Fesc), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Fstrip", HOFFSET(struct parameters, Fstrip), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "Tau0", HOFFSET(struct parameters, Tau0), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "TauS", HOFFSET(struct parameters, TauS), H5T_NATIVE_DOUBLE); + status = H5Tinsert(dtype, "TauD", HOFFSET(struct parameters, TauD), H5T_NATIVE_DOUBLE); + //Specify file type + ftype = H5Tcreate(H5T_COMPOUND, 8 * 13); + status = H5Tinsert(ftype, "M0", 0, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Epsilon0", 8, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Beta0", 16, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Gamma0", 24, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "MZ", 32, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "EpsilonZ", 40, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "BetaZ", 48, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "GammaZ", 56, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Fesc", 64, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Fstrip", 72, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "Tau0", 80, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "TauS", 88, H5T_IEEE_F64LE); + status = H5Tinsert(ftype, "TauD", 96, H5T_IEEE_F64LE); + //Set dimensions array + dims1[0] = 1; + //Set the path name for the parameters + sprintf(path,"/Universe_%06d/Model_Parameters",iuniverse); + //Create dataspace and dataset, and write the compound + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, ftype, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, dtype, H5S_ALL, H5S_ALL, H5P_DEFAULT, &P); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + // Close the file type + status = H5Tclose(ftype); + // Close the data type + status = H5Tclose(dtype); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Chi2 values // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + //Allocate the chi2 array for All.Nobs values plus the total value + chi2 = emalloc("CHI2", (All.Nobs + 1) * sizeof(float)); + //Compute Chi2 + get_chi2(chi2); + //Set dimensions array + dims1[0] = All.Nobs+1; + //Set the path name for the chi2 output + sprintf(path,"/Universe_%06d/Chi2",iuniverse); + //Create dataspace and dataset, and write the dataset + dspace = H5Screate_simple(1, dims1, NULL); + dset = H5Dcreate(stats_file, path, H5T_NATIVE_FLOAT, dspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); + status = H5Dwrite(dset, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, chi2); + // Close the dataset + status = H5Dclose(dset); + // Close the data space + status = H5Sclose(dspace); + //Free chi2 array + efree(chi2); + + ////////////////////////////////////////////////////////////////////////////////////////////////////// + // Done with this Universe // + ////////////////////////////////////////////////////////////////////////////////////////////////////// + + //Close universe group + status = H5Gclose(universe_group); + //Close the file and print to screen if writing was unsuccessful + status = H5Fclose(stats_file); + if (status != 0) printf("%s Task %5d failed to write to file %s\n",All.startline,ThisTask,outfname); + +} +#endif + + +/*! \brief This function writes all chi2 values of all universes + * + */ +void write_statistics_chi2(char *outdir, int step) +{ + if (All.OutputFormat == 2) +#ifdef HDF5_SUPPORT + //For HDF5 the chi2 values are written directly into each universe + return; +#else + write_statistics_chi2_ascii(outdir, step); +#endif + else if (All.OutputFormat == 1) write_statistics_chi2_ascii(outdir, step); + else write_statistics_chi2_ascii(outdir, step); +} + + +/*! \brief This function writes all chi2 values of all universes into an ascii file + * + */ +void write_statistics_chi2_ascii(char *outdir, int step) +{ + char outfname[NSTRING]; + int i,itask; + float *chi2; + FILE *ofp; + MPI_Status status; + + //The master task now do the work + if (ThisTask == MasterTask) + { + //Allocate the chi2 array for All.Nobs values plus the total value + chi2 = emalloc("CHI2", (All.Nobs + 1) * sizeof(float)); + //Compute Chi2 + get_chi2(chi2); + //Get the file name + sprintf(outfname,"%s/chi2.out",outdir); + + //Task 0 creates the file and writes its chi2 values + if (ThisTask == 0) + { //Create Chi2^2 file + ofp = fopen(outfname,"a"); + fprintf(ofp,"# TOTAL"); +#ifdef READ_SMF + fprintf(ofp," SMF"); +#endif +#ifdef READ_FQ + fprintf(ofp," FQ"); +#endif +#ifdef READ_CSFRD + fprintf(ofp," CSFRD"); +#endif +#ifdef READ_SSFR + fprintf(ofp," SSFR"); +#endif +#ifdef READ_WP + fprintf(ofp," WP"); +#endif + fprintf(ofp,"\n"); + fprintf(ofp,"%d %f",step,chi2[All.Nobs]); + for (i = 0; i < All.Nobs; i++) fprintf(ofp," %f",chi2[i]); + fprintf(ofp,"\n"); + fclose(ofp); + } + + //Loop over all other master tasks + for (itask = All.NTaskPerUniverse; itask < NTask; itask += All.NTaskPerUniverse) + { //Task 0 revceives the chi2 values and prints them to the file + if (ThisTask == 0) + { + MPI_Recv(chi2, All.Nobs + 1, MPI_FLOAT, itask, TAG_PROB, MPI_COMM_WORLD, &status); + ofp = fopen(outfname,"a"); + fprintf(ofp,"%d %f",step+itask/All.NTaskPerUniverse,chi2[All.Nobs]); + for (i = 0; i < All.Nobs; i++) fprintf(ofp," %f",chi2[i]); + fprintf(ofp,"\n"); + fclose(ofp); + } + //All other master tasks send their chi2 values to task 0 + else MPI_Ssend(chi2, All.Nobs + 1, MPI_FLOAT, 0, TAG_PROB, MPI_COMM_WORLD); + }//Loop over master tasks done + + //Free the chi2 array + efree(chi2); + } + +} + + +/*! \brief This function prints all walker statistics to either ascii or hdf5 output files + * + */ +void write_walker_statistics(void) +{ + const int nline = 500; + char outfname[NSTRING], infname[NSTRING], line[nline], *tmp; + int i,j,k,itask,Nfilepar,Nfilewalk,Ncoldwalkers,Nsteps,Nleft; + double temp; + double *x; //Walkers, Length: Nwalkers*Nparam + double *y; //Walkers, Length: NUniverses*Nparam + FILE *ifp; + + //Allocate Arrays + x = emalloc_movable(&x, "X", All.Nwalkers * All.Nparam * sizeof(double)); + y = emalloc_movable(&y, "Y", All.NUniverses * All.Nparam * sizeof(double)); + + //Only the main task opens the walker file and prints to screen + if (ThisTask == 0) + { + //Open the walker file + if (All.Mode == 12) + { + sprintf(infname,"%s/walkers.mcmc.%d.out",All.OutputDir,All.Seed); + } + else if (All.Mode == 22) + { + sprintf(infname,"%s/walkers.hybrid.%d.out",All.OutputDir,All.Seed); + } + else if (All.Mode == 32) + { + sprintf(infname,"%s/walkers.pt.%d.out",All.OutputDir,All.Seed); + } + else + { + sprintf(line, "Mode %d not available...",All.Mode); + endrun(line); + } + + if(!(ifp=fopen(infname,"r"))) + { + sprintf(line, "Cannot open file `%s' for reading the walkers.", infname); + endrun(line); + } + + //Get first line of walker file + fgets(line,nline,ifp); + sscanf(line,"#Walkers/Parameters/seed/i %d %d\n", &Nfilewalk, &Nfilepar); + + //Sanity Checks + if (Nfilewalk != All.Nwalkers) + { + sprintf(line, "The walker file %s contains %d walkers (while the code needs %d)",infname,Nfilewalk,All.Nwalkers); + endrun(line); + } + if (Nfilepar != All.Nparam) + { + sprintf(line, "The walker file %s contains %d parameters (while the code needs %d)",infname,Nfilepar,All.Nparam); + endrun(line); + } + + //Print what is done... + printf("%s\n%s Computing statistics for each walker...\n",All.fullline,All.startline); + printf("%s Reading %d Walkers from %s...\n",All.startline,All.Nwalkers,infname); + + //Initialise the number of cold walkers + Ncoldwalkers = 0; + + //Get each line in the file + for (k = 0; k < All.Nwalkers; k++) + { //Get the new line + fgets(line,nline,ifp); + tmp = strtok(line, " "); + //Check for parallel tempering + if (All.Mode == 32) + { //Get the temperature + tmp = strtok(NULL, " "); + temp = atof(tmp); + //Get the scaling + tmp = strtok(NULL, " "); + //Get the acceptance count + tmp = strtok(NULL, " "); + } //If not parallel tempering... + else + { //... set temperature to 1 + temp = 1.0; + } + //Skip if not cold walker + if (temp > 1.0) continue; + Ncoldwalkers++; + //Get all the parameters for the walker + for (i = 0; i < All.Nparam; i++) + { + tmp = strtok(NULL, " "); + x[k*All.Nparam+i] = atof(tmp); + } + } //End reading walker file + fclose(ifp); + } + + //Broadcast the number of cold walkers from task 0 + MPI_Bcast(&Ncoldwalkers, 1, MPI_INT, 0, MPI_COMM_WORLD); + + //Reallocate walker array + x = (double *) erealloc_movable(x, Ncoldwalkers * All.Nparam * sizeof(double)); + + //Broadcast the walker array x from task 0 + MPI_Bcast(x, Ncoldwalkers * All.Nparam, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + //Calculate the number of steps that have to be taken, and the number of walkers left afterwards + Nsteps = (int)((double)(Ncoldwalkers) / (double)(All.NUniverses)); + Nleft = Ncoldwalkers % All.NUniverses; + + //Check format to see which files to create + if (All.OutputFormat == 2) +#ifdef HDF5_SUPPORT + write_walker_statistics_init_hdf5(outfname); +#else + write_walker_statistics_init_ascii(outfname); +#endif + else if (All.OutputFormat == 1) write_walker_statistics_init_ascii(outfname); + else write_walker_statistics_init_ascii(outfname); + + //Go through all steps + for (k = 0; k < Nsteps; k++) + { //For each step go through all universes + for (j = 0; j < All.NUniverses; j++) + { //Go through each task in the universe + for (itask = MasterTask; itask < MasterTask + All.NTaskPerUniverse; itask++) + { //If this task is in the current universe + if (itask == ThisTask && j == MasterTask / All.NTaskPerUniverse) + {//Go through all parameters + for (i = 0; i < All.Nparam; i++) + { //Write the corresponding walker parameters to y + y[j*All.Nparam+i] = x[(k*All.NUniverses+j)*All.Nparam+i]; + }//Params + }//ThisTask + }//TaskPerUniverse + }//Universes + //Calculate statistics for these universes + lnprob(y); + //Write all chi2 values to the output file + write_statistics_chi2(outfname, k * All.NUniverses); + //Go through all universes again + for (j = 0; j < All.NUniverses; j++) + { //If this task is the master task of the current universe + if (ThisTask == MasterTask && j == MasterTask / All.NTaskPerUniverse) + { //Check which format to use + if (All.OutputFormat == 2) +#ifdef HDF5_SUPPORT + write_walker_statistics_hdf5(outfname, k*All.NUniverses+j); +#else + write_walker_statistics_ascii(outfname, k*All.NUniverses+j); +#endif + else if (All.OutputFormat == 1) write_walker_statistics_ascii(outfname, k*All.NUniverses+j); + else write_walker_statistics_ascii(outfname, k*All.NUniverses+j); + }//ThisTask + //Make the other tasks wait until writing is completed + MPI_Barrier(MPI_COMM_WORLD); + }//Universes + }//Steps + + //Reallocate walker array + y = (double *) erealloc_movable(y, Nleft * All.Nparam * sizeof(double)); + + //Go through remaning universes + for (j = 0; j < Nleft; j++) + { //Go through each task in the universe + for (itask = MasterTask; itask < MasterTask + All.NTaskPerUniverse; itask++) + { //If this task is in the current universe + if (itask == ThisTask && j == MasterTask / All.NTaskPerUniverse) + {//Go through all parameters + for (i = 0; i < All.Nparam; i++) + { //Write the corresponding walker parameters to y + y[j*All.Nparam+i] = x[(Nsteps*All.NUniverses+j)*All.Nparam+i]; + }//Params + }//ThisTask + }//TaskPerUniverse + }//Universes + //Calculate statistics for these universes + lnprob(y); + //Write all chi2 values to the output file + write_statistics_chi2(outfname, Nsteps * All.NUniverses); + //Go through all universes again + for (j = 0; j < Nleft; j++) + { //If this task is the master task of the current universe + if (ThisTask == MasterTask && j == MasterTask / All.NTaskPerUniverse) + { //Check which format to use + if (All.OutputFormat == 2) +#ifdef HDF5_SUPPORT + write_walker_statistics_hdf5(outfname, k*All.NUniverses+j); +#else + write_walker_statistics_ascii(outfname, k*All.NUniverses+j); +#endif + else if (All.OutputFormat == 1) write_walker_statistics_ascii(outfname, k*All.NUniverses+j); + else write_walker_statistics_ascii(outfname, k*All.NUniverses+j); + }//ThisTask + //Make the other tasks wait until writing is completed + MPI_Barrier(MPI_COMM_WORLD); + }//Universes + + //Free walker arrays + efree_movable(y); + efree_movable(x); + +} + + +/*! \brief This function generates the folder for the walker statistiscs ascii files if needed and set the directory path + * + */ +void write_walker_statistics_init_ascii(char *outfname) +{ + FILE *ofp; + + //Set the directory name for all tasks + sprintf(outfname,"%s/walkerstats/%d",All.OutputDir,All.Seed); + + //Task 0 creates new directory if needed + if (ThisTask == 0) + { //Print what's done to the screen + printf("%s\n%s Writing walker statistics in folder %s\n",All.fullline,All.startline,outfname); + //Get name of directory + sprintf(outfname,"%s/walkerstats",All.OutputDir); + //Create walkerstats directory if needed + if (mkdir(outfname, 02755) == 0) + { + if (All.verbose >= VERBOSE_MIN) printf("%s\n%s Creating walker statistics directory '%s'\n",All.fullline,All.startline,outfname); + } + //Get name of directory + sprintf(outfname,"%s/walkerstats/%d",All.OutputDir,All.Seed); + //Create seed name directory if needed + if (mkdir(outfname, 02755) == 0) + { + if (All.verbose >= VERBOSE_MIN) printf("%s\n%s Creating walker statistics directory '%s'\n",All.fullline,All.startline,outfname); + } + + //Create the chi2 file + sprintf(outfname,"%s/walkerstats/%d/chi2.out",All.OutputDir,All.Seed); + ofp = fopen(outfname,"w"); + fclose(ofp); + + //Reset the directory name + sprintf(outfname,"%s/walkerstats/%d",All.OutputDir,All.Seed); + + }//End task 0 + +} + + +#ifdef HDF5_SUPPORT +/*! \brief This function generates the HDF5 file for the walker statistiscs and set the file name + * + */ +void write_walker_statistics_init_hdf5(char *outfname) +{ + hid_t stats_file; + herr_t status; + + //Set the HDF5 file name + if (All.Mode == 12) sprintf(outfname,"%s/stats.mcmc.%d.h5", All.OutputDir,All.Seed); + else if (All.Mode == 22) sprintf(outfname,"%s/stats.hybrid.%d.h5",All.OutputDir,All.Seed); + else if (All.Mode == 32) sprintf(outfname,"%s/stats.pt.%d.h5", All.OutputDir,All.Seed); + else sprintf(outfname,"%s/stats.%d.h5", All.OutputDir,All.Seed); + //Create file + if (ThisTask == 0) + { //Print what's done to the screen + printf("%s\n%s Writing walker statistics to file %s\n",All.fullline,All.startline,outfname); + //Create statistics file by opening and closing + stats_file = H5Fcreate(outfname, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT); + status = H5Fclose(stats_file); + //Warn if there are problems + if (status != 0) printf("%s Task 0 failed to create file %s\n",All.startline,outfname); + } + +} +#endif + + +/*! \brief This function calls write_statistics_ascii() and prints to screen + * + */ +void write_walker_statistics_ascii(char *outfname, int iuniverse) +{ //Print to screen what is done (use ascii) + printf("%s Writing Universe %3d to folder %s\n",All.startline,iuniverse,outfname); + //Write this universe to the HDF5 file + write_statistics_ascii(outfname, iuniverse); +} + + +#ifdef HDF5_SUPPORT +/*! \brief This function calls write_statistics_hdf5() and prints to screen + * + */ +void write_walker_statistics_hdf5(char *outfname, int iuniverse) +{ //Print to screen what is done (use HDF5) + printf("%s Writing Universe %3d to file %s\n",All.startline,iuniverse,outfname); + //Write this universe to the HDF5 file + write_statistics_hdf5(outfname, iuniverse); +} +#endif diff --git a/tools/consistent_trees_to_emerge.sh b/tools/consistent_trees_to_emerge.sh new file mode 100755 index 0000000..5e15090 --- /dev/null +++ b/tools/consistent_trees_to_emerge.sh @@ -0,0 +1,21 @@ +#!/bin/sh +################################################################################################ +# Emerge code tools - consistent_trees_to_emerge.sh # +# Call with and , e.g. for tree_0_0_0.dat to tree_1_1_1.dat: # +# consistent_trees_to_emerge.sh tree 2 # +################################################################################################ + +FILENAME="$1" +BOX_DIVISIONS=$2 + +A=0 +for ((i=0; i<$BOX_DIVISIONS; i++)) do + for ((j=0; j<$BOX_DIVISIONS; j++)) do + for ((k=0; k<$BOX_DIVISIONS; k++)) do + + ./convert_CT_to_emerge tree_${i}_${j}_${k}.dat ${FILENAME}.${A} + + ((A++)) + done + done +done diff --git a/tools/convert_CT_to_emerge.c b/tools/convert_CT_to_emerge.c new file mode 100644 index 0000000..a36a11c --- /dev/null +++ b/tools/convert_CT_to_emerge.c @@ -0,0 +1,441 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Emerge code tools - File convert_CT_to_emerge.c // +// Converts a standard output file from consistent-trees (e.g. tree_0_0_0.dat) to the emerge // +// tree format. Call as: // +// convert_CT_to_emerge // +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Enable LONGIDS if IDs are larger than 4e9 +// #define LONGIDS +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +#define BLKLEN fwrite(&blklen, sizeof(int), 1, ofp); + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Check that all variables are at the right column! +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Variable Column_Number +/////////////////////////////////////////////////////////////////////////////////////////////////// +#define SCALE 0 +#define HALOID 1 +#define DESCID 3 +#define NP 4 +#define UPID 6 +#define MVIR 10 +#define RVIR 11 +#define RS 12 +#define MMP 14 +#define POSX 17 +#define POSY 18 +#define POSZ 19 +#define VELX 20 +#define VELY 21 +#define VELZ 22 +#define LAMBDA 26 +#define BFID 27 + +#ifndef LONGIDS +typedef unsigned int IDType; +#else +typedef unsigned long long IDType; +#endif + +//Declarations +struct haloin +{ + unsigned short np; + unsigned short mmp; + + long long haloid; + long long descid; + long long upid; + long long bfid; + + float scale; + float mvir; + float rvir; + float rs; + float lambda; + + float pos[3]; + float vel[3]; +}; + +struct haloout +{ + IDType haloid; + IDType descid; + IDType upid; + + unsigned short np; + unsigned short mmp; + + float scale; + float mvir; + float rvir; + float c; + float lambda; + + float pos[3]; + float vel[3]; +}; + +//Function declarations +int compare_scale(const void *a, const void *b); +void assign_value(char *value, struct haloin *h, int column); +void loadBar(int x, int n, int r, int w); + + +//Main code +int main(int argc, char **argv) +{ + + int i,j,blklen; + int itree, ihalo; + int Ntrees,Nhalostot; + int Nmaxhalo; + int *Nhalos; + + IDType *TreeID; + + long long MaxID; + + FILE *ifp, *ofp; + + const int linesize = 1000; + char line[linesize], symbol; + char *infname, *outfname, *tmp; + + struct haloin *hread; + struct haloout hout; + + // Read input and output files + if (argc < 3) + { + printf("Enter input file name and output file name!\n"); + exit(0); + } + + infname = calloc(200,sizeof *infname); + outfname = calloc(200,sizeof *outfname); + + sprintf(infname, "%s",argv[1]); + sprintf(outfname,"%s",argv[2]); + + Ntrees=0; + Nhalostot=0; + Nmaxhalo=0; + ihalo=0; + + MaxID = (1UL << (sizeof(unsigned int)*8)) - 1; + + // Check sizes of types on this machine + if(sizeof(long long) != 8) + { + printf("Type `long long' is not 64 bit on this platform.\n"); + exit(0); + } + if(sizeof(int) != 4) + { + printf("Type `int' is not 32 bit on this platform.\n"); + exit(0); + } + if(sizeof(unsigned long long) != 8) + { + printf("Type `unsigned long long' is not 64 bit on this platform.\n"); + exit(0); + } + if(sizeof(unsigned int) != 4) + { + printf("Type `unsigned int' is not 32 bit on this platform.\n"); + exit(0); + } + if(sizeof(float) != 4) + { + printf("Type `float' is not 32 bit on this platform.\n"); + exit(0); + } + if(sizeof(double) != 8) + { + printf("Type `double' is not 64 bit on this platform.\n"); + exit(0); + } + + tmp = calloc(200,sizeof *tmp); + sprintf(tmp, "%s to %s",infname,outfname); + + printf("\n**********************************************************************\n"); + printf("* *\n"); + printf("* TreeConverter: Converts Consistent-Trees file into Emerge format *\n"); + printf("* *\n"); + printf("**********************************************************************\n"); + printf("* *\n"); + printf("* Converting %-55s *\n",tmp); + printf("* *\n"); + printf("**********************************************************************\n"); + printf("* *\n"); + printf("* Checking the number of haloes in the tree file... *\n"); + + free(tmp); + + //Count all haloes in file + ifp=fopen(infname,"r"); + + //Skip Header + i=0; + while ((fgets(line,linesize,ifp))&&(line[0]=='#')) i++; + + //Read Number of trees + sscanf(line,"%d\n",&Ntrees); + + //Allocate Nhalos + if(!(Nhalos=calloc(Ntrees,sizeof *Nhalos))) + { + fprintf(stderr,"Failed to allocate memory for Nhalos.\n"); + exit(0); + } + + //Allocate TreeID + if(!(TreeID=calloc(Ntrees,sizeof *TreeID))) + { + fprintf(stderr,"Failed to allocate memory for TreeID.\n"); + exit(0); + } + + //Go through file and count number of haloes per tree + itree = ihalo = 0; + while (fgets(line,linesize,ifp)) + { + if (line[0]=='#'){ + if (ihalo>0) itree++; +#ifndef LONGIDS + sscanf(line,"%*s %u\n",&TreeID[itree]); +#else + sscanf(line,"%*s %llu\n",&TreeID[itree]); +#endif + if (ihalo>Nmaxhalo) Nmaxhalo=ihalo; + ihalo=0; + loadBar(itree, Ntrees, 50, 59); + } + if (line[0]!='#') + { + Nhalostot++; + ihalo++; + Nhalos[itree] = ihalo; + } + } + itree++; + fclose(ifp); + + if (itree!=Ntrees){ + printf("* Error! The number of trees stated in the file is unequal to the actual number of trees!\n"); + exit(0); + } + + printf("* ... done checking the number of haloes *\n"); + printf("* *\n"); + printf("* The file contains %10d trees and %10d haloes! *\n",Ntrees,Nhalostot); + printf("* *\n"); + printf("**********************************************************************\n"); + + if(!(hread=malloc(Nmaxhalo*sizeof(struct haloin)))) + { + fprintf(stderr,"Failed to allocate memory.\n"); + exit(0); + } + + ofp=fopen(outfname,"w"); + + //Write Header containing the number of trees... + blklen=sizeof(int); + BLKLEN; + fwrite(&Ntrees, sizeof(int), 1, ofp); + BLKLEN; + + //...the number of haloes for each tree... + blklen=Ntrees*sizeof(int); + BLKLEN; + fwrite(Nhalos, sizeof(int), Ntrees, ofp); + BLKLEN; + + //...and the Ids of the trees + blklen=Ntrees*sizeof(int); + BLKLEN; + fwrite(TreeID, sizeof(IDType), Ntrees, ofp); + BLKLEN; + + printf("* *\n"); + printf("* Loading haloes... *\n"); + + itree=0; + + ifp=fopen(infname,"r"); + free(infname); + i=0; + while ((fgets(line,linesize,ifp))&&(line[0]=='#')) i++; + sscanf(line,"%d\n",&Ntrees); + fgets(line,linesize,ifp); + + //Write number of bytes for all haloes + blklen=Nhalostot*sizeof(struct haloout); + BLKLEN; + + // Loop over all trees in file + while(itree MaxID) + { + printf("* Halo at position %d has an ID of %lld.\n",i,hread[i].haloid); + printf("* The maximum allowed value for integers is %lld.\n",MaxID); + printf("* Use the option LONGIDS instead (also for EMERGE)!\n"); + exit(0); + } +#endif + //Assign values to output halo + hout.np = hread[i].np; + hout.mmp = hread[i].mmp; + hout.scale = hread[i].scale; + hout.mvir = hread[i].mvir; + hout.rvir = hread[i].rvir; + hout.c = hread[i].rvir/hread[i].rs; + hout.lambda = hread[i].lambda; + hout.pos[0] = hread[i].pos[0]; + hout.pos[1] = hread[i].pos[1]; + hout.pos[2] = hread[i].pos[2]; + hout.vel[0] = hread[i].vel[0]; + hout.vel[1] = hread[i].vel[1]; + hout.vel[2] = hread[i].vel[2]; + hout.haloid = (IDType) (hread[i].haloid + 1); //Convert to unsigned (need to avoid -1) + hout.descid = (IDType) (hread[i].descid + 1); + hout.upid = (IDType) (hread[i].upid + 1); + + //Write halo to output file + fwrite(&hout, sizeof(struct haloout), 1, ofp); + } + + //Next tree + itree++; + + loadBar(itree, Ntrees, 50, 59); + + } + + BLKLEN; + + printf("* ... done loading haloes *\n"); + printf("* *\n"); + printf("**********************************************************************\n"); + printf("* *\n"); + printf("* Written to file %40s *\n",outfname); + printf("* *\n"); + printf("**********************************************************************\n"); + + free(TreeID); + free(Nhalos); + free(outfname); + fclose(ifp); + fclose(ofp); + free(hread); + +} + + +// Process has done i out of n rounds, +// and we want a bar of width w and resolution r. +void loadBar(int x, int n, int r, int w) +{ + if (nscale = strtof(value, NULL); + if (column == HALOID) h->haloid = strtoll(value, NULL, 0); + if (column == DESCID) h->descid = strtoll(value, NULL, 0); + if (column == NP) h->np = (unsigned short) strtoul(value, NULL, 0); + if (column == UPID) h->upid = strtoll(value, NULL, 0); + if (column == MVIR) h->mvir = strtof(value, NULL); + if (column == RVIR) h->rvir = strtof(value, NULL); + if (column == RS) h->rs = strtof(value, NULL); + if (column == MMP) h->mmp = (unsigned short) strtoul(value, NULL, 0); + if (column == POSX) h->pos[0] = strtof(value, NULL); + if (column == POSY) h->pos[1] = strtof(value, NULL); + if (column == POSZ) h->pos[2] = strtof(value, NULL); + if (column == VELX) h->vel[0] = strtof(value, NULL); + if (column == VELY) h->vel[1] = strtof(value, NULL); + if (column == VELZ) h->vel[2] = strtof(value, NULL); + if (column == LAMBDA) h->lambda = strtof(value, NULL); + if (column == BFID) h->bfid = strtoll(value, NULL, 0); + +} + + +//Compare the scale factors of two haloes +//If they are the same compare the BFID of the haloes +//This is done to make the sorting stable +int compare_scale(const void *a, const void *b) +{ + const struct haloin *c = a; + const struct haloin *d = b; + int scale = (c->scale > d->scale) - (c->scale < d->scale); + return scale != 0 ? scale : d->bfid - c->bfid; +} + + + diff --git a/trees/.gitignore b/trees/.gitignore new file mode 100644 index 0000000..86d0cb2 --- /dev/null +++ b/trees/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/trees/README b/trees/README new file mode 100644 index 0000000..840e601 --- /dev/null +++ b/trees/README @@ -0,0 +1,11 @@ +Dark matter halo merger trees extracted from a simulation with 100 Mpc side length can be downloaded here: + +http://www.usm.lmu.de/emerge + +These trees have the following parameters: +HubbleParam 0.6781 +Omega0 0.3080 +OmegaLambda 0.6920 +OmegaBaryon 0.0484 +BoxSize 67.81 + diff --git a/user-guide.pdf b/user-guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ff573aa53536d43ccd1d821954e9b983fdd5418c GIT binary patch literal 464563 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ξ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 sw%)qt#;l4QhK60U2M9Z0nNF^;Jiht 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 W-}`mA?*)aOnFWrFOhir;?tcNd(c>oo literal 0 HcmV?d00001