Page MenuHomec4science

No OneTemporary

File Metadata

Created
Sun, Feb 23, 06:27
This file is larger than 256 KB, so syntax highlighting was skipped.
This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
diff --git a/Makefile b/Makefile
index 3306ce4..f8f1ed6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,876 +1,880 @@
#----------------------------------------------------------------------
# From the list below, please activate/deactivate the options that
# apply to your run. If you modify any of these options, make sure
# that you recompile the whole code by typing "make clean; make".
#
# Look at end of file for a brief guide to the compile-time options.
#----------------------------------------------------------------------
#--------------------------------------- Basic operation mode of code
#OPT += -DPERIODIC
OPT += -DUNEQUALSOFTENINGS
#--------------------------------------- Things that are always recommended
OPT += -DPEANOHILBERT
OPT += -DWALLCLOCK
#--------------------------------------- TreePM Options
#OPT += -DPMGRID=128
#OPT += -DPLACEHIGHRESREGION=3
#OPT += -DENLARGEREGION=1.2
#OPT += -DASMTH=1.25
#OPT += -DRCUT=4.5
#--------------------------------------- Single/Double Precision
#OPT += -DDOUBLEPRECISION
#OPT += -DDOUBLEPRECISION_FFTW
#--------------------------------------- Time integration options
OPT += -DSYNCHRONIZATION
#OPT += -DFLEXSTEPS
#OPT += -DPSEUDOSYMMETRIC
OPT += -DNOSTOP_WHEN_BELOW_MINTIMESTEP
#OPT += -DNOPMSTEPADJUSTMENT
#--------------------------------------- Output
OPT += -DADVANCEDSTATISTICS
OPT += -DADVANCEDCPUSTATISTICS
OPT += -DSYSTEMSTATISTICS
OPT += -DBLOCK_SKIPPING
#OPT += -DHAVE_HDF5
#OPT += -DOUTPUTPOTENTIAL
#OPT += -DOUTPUTACCELERATION
#OPT += -DOUTPUTCHANGEOFENTROPY
#OPT += -DOUTPUTTIMESTEP
#OPT += -DOUTPUTERADSTICKY
#OPT += -DOUTPUTERADFEEDBACK
#OPT += -DOUTPUTENERGYFLUX
#OPT += -DOUTPUTOPTVAR1
#OPT += -DOUTPUTOPTVAR2
OPT += -DOUTPUTSTELLAR_PROP
#--------------------------------------- Things for special behaviour
#OPT += -DNOGRAVITY
#OPT += -DNOTREERND
#OPT += -DNOTYPEPREFIX_FFTW
#OPT += -DLONG_X=60
#OPT += -DLONG_Y=5
#OPT += -DLONG_Z=0.2
#OPT += -DTWODIMS
#OPT += -DSPH_BND_PARTICLES
#OPT += -DNOVISCOSITYLIMITER
OPT += -DCOMPUTE_POTENTIAL_ENERGY
#OPT += -DLONGIDS
#OPT += -DISOTHERM_EQS
#OPT += -DADAPTIVE_GRAVSOFT_FORGAS
#OPT += -DSELECTIVE_NO_GRAVITY=2+4+8+16
#OPT += -DAVOIDNUMNGBPROBLEM
#OPT += -DLIMIT_DVEL=1.0
#OPT += -DOTHERINFO
#OPT += -DDOMAIN_AT_ORIGIN
OPT += -DNO_NEGATIVE_PRESSURE
#OPT += -DCOMPUTE_VELOCITY_DISPERSION
#OPT += -DCYLINDRICAL_SYMMETRY
OPT += -DWRITE_ALL_MASSES
OPT += -DENTROPYPRED
OPT += -DCOUNT_ACTIVE_PARTICLES
OPT += -DRANDOMSEED_AS_PARAMETER
OPT += -DDETAILED_CPU
OPT += -DDETAILED_CPU_GRAVITY
OPT += -DDETAILED_CPU_DOMAIN
-OPT += -DDETAILED_OUTPUT_IN_GRAVTREE
+OPT += -DDETAILED_CPU_OUTPUT_IN_GRAVTREE
+OPT += -DDETAILED_CPU_OUTPUT_IN_HYDRA
+OPT += -DDETAILED_CPU_OUTPUT_IN_DENSITY
+OPT += -DDETAILED_CPU_OUTPUT_IN_STARS_DENSITY
+OPT += -DDETAILED_CPU_OUTPUT_IN_CHIMIE
#OPT += -DSPLIT_DOMAIN_USING_TIME
OPT += -DCOSMICTIME
OPT += -DONLY_MASTER_READ_EWALD
#OPT += -DPNBODY
#OPT += -DPNBODY_OUTPUT_POS
#OPT += -DPNBODY_OUTPUT_VEL
#OPT += -DPNBODY_OUTPUT_NUM
#OPT += -DPNBODY_OUTPUT_MASS
#OPT += -DPNBODY_OUTPUT_TYPE
#OPT += -DPNBODY_OUTPUT_ENERGY
#OPT += -DPNBODY_OUTPUT_DENSITY
#OPT += -DPNBODY_OUTPUT_HSML
#OPT += -DPNBODY_OUTPUT_METALS
#--------------------------------------- Physical processes
OPT += -DCOOLING
#OPT += -DIMPLICIT_COOLING_INTEGRATION
#OPT += -DDO_NO_USE_HYDROGEN_MASSFRAC_IN_COOLING
#OPT += -DHEATING
#OPT += -DHEATING_PE # photo-electric heating
OPT += -DSFR
OPT += -DCOMPUTE_SFR_ENERGY
OPT += -DSFR_NEG_DIV_ONLY
OPT += -DSTELLAR_PROP
OPT += -DCHIMIE # need stellar prop
OPT += -DCHIMIE_THERMAL_FEEDBACK
OPT += -DCHIMIE_COMPUTE_THERMAL_FEEDBACK_ENERGY
#OPT += -DCHIMIE_KINETIC_FEEDBACK
#OPT += -DCHIMIE_COMPUTE_KINETIC_FEEDBACK_ENERGY
OPT += -DCHIMIE_EXTRAHEADER
OPT += -DCHIMIE_INPUT_ALL
OPT += -DCHIMIE_MC_SUPERNOVAE
#OPT += -DFEEDBACK
#OPT += -DFEEDBACK_WIND
#--------------------------------------- multiphase
#OPT += -DMULTIPHASE
#OPT += -DNO_HYDRO_FOR_GAS # do not use hydro routine (at all)
#OPT += -DNO_DENSITY_FOR_STICKY # do not compute density in sticky (need to be done in sfr)
#OPT += -DPHASE_MIXING # need MULTIPHASE : enable phase mixing
#OPT += -DCOLDGAS_CYCLE # need MULTIPHASE and PHASE_MIXING
#OPT += -DEXTERNAL_FLUX
#OPT += -DSTELLAR_FLUX
#OPT += -DCOUNT_COLLISIONS # count sticky collisions
#--------------------------------------- Outer potential
#OPT += -DOUTERPOTENTIAL
#OPT += -DNFW
#OPT += -DPISOTHERM
#OPT += -DPLUMMER
OPT += -DMIYAMOTONAGAI
#OPT += -DCORIOLIS
#--------------------------------------- Testing and Debugging options
#OPT += -DFORCETEST=0.1
#OPT += -DWITH_ID_IN_HYDRA
#OPT += -DPARTICLE_FLAG
#OPT += -DOUTPUT_EVERY_TIMESTEP
#OPT += -DOUTPUT_COOLING_FUNCTION
OPT += -DCHECK_BLOCK_ORDER
OPT += -DCHECK_ENTROPY_SIGN
OPT += -DCHECK_TYPE_DURING_IO
OPT += -DCHECK_ID_CORRESPONDENCE
#--------------------------------------- Glass making
#OPT += -DMAKEGLASS=262144
#--------------------------------------- Artificial Conductivity
#OPT += -DART_CONDUCTIVITY
#OPT += -DOUTPUT_CONDUCTIVITY
#OPT += -DOUTPUTOPTVAR1
#OPT += -DOUTPUTOPTVAR2
#--------------------------------------- Agn
#OPT += -DBUBBLES
#OPT += -DAGN_ACCRETION
#OPT += -DAGN_FEEDBACK
#OPT += -DAGN_USE_ANGULAR_MOMENTUM
#OPT += -DAGN_HEATING
#OPT += -DBONDI_ACCRETION
#OPT += -DUSE_BONDI_POWER
#----------------------------------------------------------------------
# Here, select compile environment for the target machine. This may need
# adjustment, depending on your local system. Follow the examples to add
# additional target platforms, and to get things properly compiled.
#----------------------------------------------------------------------
#--------------------------------------- Select some defaults
CC = mpicc # sets the C-compiler
OPTIMIZE = -O2 -Wall -g # sets optimization and warning flags
MPICHLIB = -lmpich
#--------------------------------------- Select target computer
SYSTYPE="obscalc"
#SYSTYPE="callisto-intel"
#SYSTYPE="bg1"
#SYSTYPE="obsds"
#SYSTYPE="leo_openmpi"
#SYSTYPE="leo_mpich2shm"
#SYSTYPE="graphor0"
#SYSTYPE="obsrevaz"
#SYSTYPE="regor_openmpigcc"
#SYSTYPE="regor_mvapich2gcc"
#SYSTYPE="meso_mpich2"
#SYSTYPE="meso"
#SYSTYPE="revaz/local"
#SYSTYPE="revaz/local_mpich2"
#SYSTYPE="horizon3_mpich1"
#SYSTYPE="horizon3_mpich2"
#SYSTYPE="horizon3"
#SYSTYPE="LUXOR"
#SYSTYPE="MPA"
#SYSTYPE="Mako"
#SYSTYPE="Regatta"
#SYSTYPE="RZG_LinuxCluster"
#SYSTYPE="RZG_LinuxCluster-gcc"
#SYSTYPE="OpteronMPA"
#SYSTYPE="OPA-Cluster32"
#SYSTYPE="OPA-Cluster64"
#--------------------------------------- Adjust settings for target computer
# module add openmpi-x86_64
ifeq ($(SYSTYPE),"obscalc")
CC = mpicc
OPTIMIZE =
GSL_INCL =
GSL_LIBS =
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL =
HDF5LIB =
NO_FFTW_LIB = "yes"
PY_INCL = -I/usr/include/python2.6/
PY_LIB = -lpython2.6
endif
ifeq ($(SYSTYPE),"callisto-intel")
CC = mpicc
OPTIMIZE =
GSL_INCL = -I/u1/yrevaz/local/gsl-intel/include
GSL_LIBS = -L/u1/yrevaz/local/gsl-intel/lib
FFTW_INCL= -I/u1/yrevaz/local/fftw-2.1.5-intel/include
FFTW_LIBS= -L/u1/yrevaz/local/fftw-2.1.5-intel/lib
MPICHLIB =
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"bg1")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
GSL_INCL = -I/home/yrevaz/local/include
GSL_LIBS = -L/home/yrevaz/local/lib
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL =
HDF5LIB =
NO_FFTW_LIB = "yes"
endif
ifeq ($(SYSTYPE),"obsds")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
GSL_INCL =
GSL_LIBS =
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL =
HDF5LIB =
NO_FFTW_LIB = "yes"
endif
ifeq ($(SYSTYPE),"graphor0")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
GSL_INCL = -I/home/epfl/revaz/local/include
GSL_LIBS = -L/home/epfl/revaz/local/lib
FFTW_INCL= -I/home/epfl/revaz/local/include
FFTW_LIBS= -L/home/epfl/revaz/local/lib
MPICHLIB = -L/home/epfl/revaz/local/openmpi/lib -lmpi
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"obsrevaz")
CC = mpicc
OPTIMIZE = -O3 -Wall -fpack-struct
GSL_INCL =
GSL_LIBS =
FFTW_INCL= -I/home/revaz/local/include/
FFTW_LIBS= -L/home/revaz/local/lib/
MPICHLIB = -lmpi
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"regor_openmpigcc")
CC = mpicc
OPTIMIZE = -O3 -Wall -fpack-struct
GSL_INCL = -I/usr/include
GSL_LIBS = -L/usr/lib64/
FFTW_INCL= -I/home/revaz/local_mvapich2gcc/include/
FFTW_LIBS= -L/home/revaz/local_mvapich2gcc/lib/
MPICHLIB = -lmpi
HDF5INCL =
HDF5LIB =
OPT += -DMESOMACHINE
endif
ifeq ($(SYSTYPE),"regor_mpich2")
CC = mpicc
OPTIMIZE = -O3 -Wall -fpack-struct
GSL_INCL = -I/usr/include
GSL_LIBS = -L/usr/lib64/
FFTW_INCL= -I/home/revaz/local_mvapich2gcc/include/
FFTW_LIBS= -L/home/revaz/local_mvapich2gcc/lib/
MPICHLIB = -L/home/revaz/local/mpich2-1.0.6nemesis/lib/ -lmpich
HDF5INCL =
HDF5LIB =
OPT += -DMESOMACHINE
endif
ifeq ($(SYSTYPE),"regor_mvapich2gcc")
CC = mpicc
OPTIMIZE = -O3 -Wall -fpack-struct
GSL_INCL = -I/usr/include
GSL_LIBS = -L/usr/lib64/
FFTW_INCL= -I/home/revaz/local_mvapich2gcc/include/
FFTW_LIBS= -L/home/revaz/local_mvapich2gcc/lib/
MPICHLIB = -L/cvos/shared/apps/ofed/1.2.5.3/mpi/gcc/mvapich2-0.9.8-15/lib/ -lmpich
HDF5INCL =
HDF5LIB =
OPT += -DMESOMACHINE
endif
ifeq ($(SYSTYPE),"leo_openmpi")
CC = mpicc
OPTIMIZE = -O3 -Wall -fpack-struct
GSL_INCL = -I/export/revaz/local/include
GSL_LIBS = -L/export/revaz/local/lib
FFTW_INCL= -I/export/revaz/local/include
FFTW_LIBS= -L/export/revaz/local/lib
MPICHLIB = -L/usr/local/mpich2-pgi/lib -lmpi
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"leo_mpich2shm")
CC = mpicc
OPTIMIZE = -O3 -Wall -g -fpack-struct
GSL_INCL = -I/export/revaz/local/include
GSL_LIBS = -L/export/revaz/local/lib
FFTW_INCL= -I/export/revaz/local/include
FFTW_LIBS= -L/export/revaz/local/lib
MPICHLIB = -L/usr/local/mpich2-pgi/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"meso_mpich2")
CC = mpicc
OPTIMIZE = -O3 -Wall -g -fpack-struct
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/horizon1/x86_64_sl4/fftw/2.1.5/include/
FFTW_LIBS= -L/horizon1/x86_64_sl4/fftw/2.1.5/lib/
MPICHLIB = -L/home/revaz/local/mpich2-1.0.3/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"meso")
CC = mpicc
OPTIMIZE = -O3 -g
GSL_INCL =
GSL_LIBS =
FFTW_INCL= -I/horizon1/x86_64_sl4/fftw/2.1.5/include/
FFTW_LIBS= -L/horizon1/x86_64_sl4/fftw/2.1.5/lib/
MPICHLIB =
HDF5INCL =
HDF5LIB =
OPT += -DMESOMACHINE
endif
ifeq ($(SYSTYPE),"revaz/local")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/home/revaz/local/include
FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -L/home/revaz/local/mpich-1.2.5/ch_p4/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"revaz/local_mpich2")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/home/revaz/local/include
FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -L/home/revaz/local/mpich2-1.0.3/lib/ -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"LUXOR")
CC = mpicc
OPTIMIZE = -O3 -Wall -g
#GSL_INCL = -I/home/revaz/local/include
#GSL_LIBS = -L/home/revaz/local/lib
#FFTW_INCL= -I/home/revaz/local/include
#FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -L/home/revaz/local/mpich-1.2.7/ch_p4/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"horizon3")
CC = mpicc
OPTIMIZE = -O3 -Wall -g -fpack-struct
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/home/revaz/local/include
FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -llam
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"horizon3_mpich1")
CC = mpicc
OPTIMIZE = -O3 -Wall -g -fpack-struct
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/home/revaz/local/include
FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -L/home/revaz/local/mpich-1.2.7/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"horizon3_mpich2")
CC = mpicc
OPTIMIZE = -O3 -Wall -g -fpack-struct
GSL_INCL = -I/home/revaz/local/include
GSL_LIBS = -L/home/revaz/local/lib
FFTW_INCL= -I/home/revaz/local/include
FFTW_LIBS= -L/home/revaz/local/lib
MPICHLIB = -L/usr/local/mpich2-pgi/lib -lmpich
HDF5INCL =
HDF5LIB =
endif
ifeq ($(SYSTYPE),"MPA")
CC = mpicc
OPTIMIZE = -O3 -Wall
GSL_INCL = -I/usr/common/pdsoft/include
GSL_LIBS = -L/usr/common/pdsoft/lib -Wl,"-R /usr/common/pdsoft/lib"
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL =
HDF5LIB = -lhdf5 -lz
endif
ifeq ($(SYSTYPE),"OpteronMPA")
CC = mpicc
OPTIMIZE = -O3 -Wall -m64
GSL_INCL = -L/usr/local/include
GSL_LIBS = -L/usr/local/lib
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL = -I/opt/hdf5/include
HDF5LIB = -L/opt/hdf5/lib -lhdf5 -lz -Wl,"-R /opt/hdf5/lib"
endif
ifeq ($(SYSTYPE),"OPA-Cluster32")
CC = mpicc
OPTIMIZE = -O3 -Wall
GSL_INCL = -I/afs/rzg/bc-b/vrs/opteron32/include
GSL_LIBS = -L/afs/rzg/bc-b/vrs/opteron32/lib -Wl,"-R /afs/rzg/bc-b/vrs/opteron32/lib"
FFTW_INCL= -I/afs/rzg/bc-b/vrs/opteron32/include
FFTW_LIBS= -L/afs/rzg/bc-b/vrs/opteron32/lib
MPICHLIB =
HDF5INCL =
HDF5LIB = -lhdf5 -lz
endif
ifeq ($(SYSTYPE),"OPA-Cluster64")
CC = mpicc
OPTIMIZE = -O3 -Wall -m64
GSL_INCL = -I/afs/rzg/bc-b/vrs/opteron64/include
GSL_LIBS = -L/afs/rzg/bc-b/vrs/opteron64/lib -Wl,"-R /afs/rzg/bc-b/vrs/opteron64/lib"
FFTW_INCL= -I/afs/rzg/bc-b/vrs/opteron64/include
FFTW_LIBS= -L/afs/rzg/bc-b/vrs/opteron64/lib
MPICHLIB =
HDF5INCL =
HDF5LIB = -lhdf5 -lz
endif
ifeq ($(SYSTYPE),"Mako")
CC = mpicc # sets the C-compiler
OPTIMIZE = -O3 -march=athlon-mp -mfpmath=sse
GSL_INCL =
GSL_LIBS =
FFTW_INCL=
FFTW_LIBS=
MPICHLIB =
HDF5INCL =
HDF5LIB = -lhdf5 -lz
endif
ifeq ($(SYSTYPE),"Regatta")
CC = mpcc_r
OPTIMIZE = -O5 -qstrict -qipa -q64
GSL_INCL = -I/afs/rzg/u/vrs/gsl_psi64/include
GSL_LIBS = -L/afs/rzg/u/vrs/gsl_psi64/lib
FFTW_INCL= -I/afs/rzg/u/vrs/fftw_psi64/include
FFTW_LIBS= -L/afs/rzg/u/vrs/fftw_psi64/lib -q64 -qipa
MPICHLIB =
HDF5INCL = -I/afs/rzg/u/vrs/hdf5_psi64/include
HDF5LIB = -L/afs/rzg/u/vrs/hdf5_psi64/lib -lhdf5 -lz
endif
ifeq ($(SYSTYPE),"RZG_LinuxCluster")
CC = mpicci
OPTIMIZE = -O3 -ip # Note: Don't use the "-rcd" optimization of Intel's compiler! (causes code crashes)
GSL_INCL = -I/afs/rzg/u/vrs/gsl_linux/include
GSL_LIBS = -L/afs/rzg/u/vrs/gsl_linux/lib -Wl,"-R /afs/rzg/u/vrs/gsl_linux/lib"
FFTW_INCL= -I/afs/rzg/u/vrs/fftw_linux/include
FFTW_LIBS= -L/afs/rzg/u/vrs/fftw_linux/lib
HDF5INCL = -I/afs/rzg/u/vrs/hdf5_linux/include
HDF5LIB = -L/afs/rzg/u/vrs/hdf5_linux/lib -lhdf5 -lz -Wl,"-R /afs/rzg/u/vrs/hdf5_linux/lib"
endif
ifeq ($(SYSTYPE),"RZG_LinuxCluster-gcc")
CC = mpiccg
OPTIMIZE = -Wall -g -O3 -march=pentium4
GSL_INCL = -I/afs/rzg/u/vrs/gsl_linux_gcc3.2/include
GSL_LIBS = -L/afs/rzg/u/vrs/gsl_linux_gcc3.2/lib -Wl,"-R /afs/rzg/u/vrs/gsl_linux_gcc3.2/lib"
FFTW_INCL= -I/afs/rzg/u/vrs/fftw_linux_gcc3.2/include
FFTW_LIBS= -L/afs/rzg/u/vrs/fftw_linux_gcc3.2/lib
HDF5INCL = -I/afs/rzg/u/vrs/hdf5_linux/include
HDF5LIB = -L/afs/rzg/u/vrs/hdf5_linux/lib -lhdf5 -lz -Wl,"-R /afs/rzg/u/vrs/hdf5_linux/lib"
endif
ifneq (HAVE_HDF5,$(findstring HAVE_HDF5,$(OPT)))
HDF5INCL =
HDF5LIB =
endif
OPTIONS = $(OPTIMIZE) $(OPT)
EXEC = Gadget2
OBJS = main.o run.o predict.o begrun.o endrun.o global.o \
timestep.o init.o restart.o io.o \
accel.o read_ic.o ngb.o \
system.o allocate.o density.o \
gravtree.o hydra.o driftfac.o \
domain.o allvars.o potential.o \
forcetree.o peano.o gravtree_forcetest.o \
pm_periodic.o pm_nonperiodic.o longrange.o \
cooling.o agn_heating.o phase.o sticky.o outerpotential.o starformation.o \
agn_feedback.o bubbles.o bondi_accretion.o chimie.o stars_density.o cosmictime.o pnbody.o
INCL = allvars.h proto.h tags.h Makefile
CFLAGS = $(OPTIONS) $(GSL_INCL) $(FFTW_INCL) $(HDF5INCL) $(PY_INCL)
ifeq (NOTYPEPREFIX_FFTW,$(findstring NOTYPEPREFIX_FFTW,$(OPT))) # fftw installed with type prefix?
FFTW_LIB = $(FFTW_LIBS) -lrfftw_mpi -lfftw_mpi -lrfftw -lfftw
else
ifeq (DOUBLEPRECISION_FFTW,$(findstring DOUBLEPRECISION_FFTW,$(OPT)))
FFTW_LIB = $(FFTW_LIBS) -ldrfftw_mpi -ldfftw_mpi -ldrfftw -ldfftw
else
FFTW_LIB = $(FFTW_LIBS) -lsrfftw_mpi -lsfftw_mpi -lsrfftw -lsfftw
endif
endif
ifeq ($(NO_FFTW_LIB),"yes")
FFTW_LIB =
endif
LIBS = $(HDF5LIB) -g $(MPICHLIB) $(GSL_LIBS) -lgsl -lgslcblas -lm $(FFTW_LIB) $(PY_LIB)
$(EXEC): $(OBJS)
$(CC) $(OBJS) $(LIBS) -o $(EXEC)
$(OBJS): $(INCL)
clean:
rm -f $(OBJS) $(EXEC)
#-----------------------------------------------------------------------
#
# Brief guide to compile-time options of the code. More information
# can be found in the code documentation.
#
# - PERIODIC:
# Set this if you want to have periodic boundary conditions.
#
# - UNEQUALSOFTENINGS:
# Set this if you use particles with different gravitational
# softening lengths.
#
# - PEANOHILBERT:
# This is a tuning option. When set, the code will bring the
# particles after each domain decomposition into Peano-Hilbert
# order. This improves cache utilization and performance.
#
# - WALLCLOCK:
# If set, a wallclock timer is used by the code to measure internal
# time consumption (see cpu-log file). Otherwise, a timer that
# measures consumed processor ticks is used.
#
# - PMGRID:
# This enables the TreePM method, i.e. the long-range force is
# computed with a PM-algorithm, and the short range force with the
# tree. The parameter has to be set to the size of the mesh that
# should be used, (e.g. 64, 96, 128, etc). The mesh dimensions need
# not necessarily be a power of two. Note: If the simulation is
# not in a periodic box, then a FFT method for vacuum boundaries is
# employed, using an actual mesh with dimension twice(!) that
# specified by PMGRID.
#
# - PLACEHIGHRESREGION:
# If this option is set (will only work together with PMGRID), then
# the long range force is computed in two stages: One Fourier-grid
# is used to cover the whole simulation volume, allowing the
# computation of the longe-range force. A second Fourier mesh is
# placed on the region occupied by "high-resolution" particles,
# allowing the computation of an intermediate scale force. Finally,
# the force on short scales is computed with the tree. This
# procedure can be useful for "zoom-simulations", provided the
# majority of particles (the high-res particles) are occupying only
# a small fraction of the volume. To activate this option, the
# parameter needs to be set to an integer bit mask that encodes the
# particle types that make up the high-res particles.
# For example, if types 0, 1, and 4 form the high-res
# particles, set the parameter to PLACEHIGHRESREGION=19, because
# 2^0 + 2^1 + 2^4 = 19. The spatial region covered by the high-res
# grid is determined automatically from the initial conditions.
# Note: If a periodic box is used, the high-res zone may not intersect
# the box boundaries.
#
# - ENLARGEREGION:
# The spatial region covered by the high-res zone has a fixed size
# during the simulation, which initially is set to the smallest
# region that encompasses all high-res particles. Normally, the
# simulation will be interrupted if high-res particles leave this
# region in the course of the run. However, by setting this
# parameter to a value larger than one, the size of the high-res
# region can be expanded, providing a buffer region. For example,
# setting it to 1.4 will enlarge its side-length by 40% (it remains
# centered on the high-res particles). Hence, with this setting, the
# high-res region may expand or move by a limited amount.
# Note: If SYNCHRONIZATION is activated, the code will be able to
# continue even if high-res particles leave the initial high-res
# grid. In this case, the code will update the size and position of
# the grid that is placed onto the high-resolution region
# automatically. To prevent that this potentially happens every
# single PM step, one should nevertheless assign a value slightly
# larger than 1 to ENLARGEREGION.
#
# - ASMTH:
# This can be used to override the value assumed for the scale that
# defines the long-range/short-range force-split in the TreePM
# algorithm. The default value is 1.25, in mesh-cells.
#
# - RCUT:
# This can be used to override the maximum radius in which the
# short-range tree-force is evaluated (in case the TreePM algorithm
# is used). The default value is 4.5, given in mesh-cells.
#
# - DOUBLEPRECISION:
# This makes the code store and compute internal particle data in
# double precision. Note that output files are nevertheless written
# by converting the particle data to single precision.
#
# - DDOUBLEPRECISION_FFTW:
# If this is set, the code will use the double-precision version of
# FTTW, provided the latter has been explicitly installed with a
# "d" prefix, and NOTYPEPREFIX_FFTW is not set. Otherwise the
# single precision version ("s" prefix) is used.
#
# - SYNCHRONIZATION:
# When this is set, particles are kept in a binary hierarchy of
# timesteps and may only increase their timestep if the new
# timestep will put them into synchronization with the higher time
# level.
#
# - FLEXSTEPS:
# This is an alternative to SYNCHRONIZATION. Particle timesteps are
# here allowed to be integer multiples of the minimum timestep that
# occurs among the particles, which in turn is rounded down to the
# nearest power-of-two devision of the total simulated
# timespan. This option distributes particles more evenly over
# individual system timesteps, particularly once a simulation has
# run for a while, and may then result in a reduction of work-load
# imbalance losses.
#
# - PSEUDOSYMMETRIC:
# When this option is set, the code will try to "anticipate"
# timestep changes by extrapolating the change of the acceleration
# into the future. This can in certain idealized cases improve the
# long-term integration behaviour of periodic orbits, but should
# make little or no difference in most real-world applications. May
# only be used together with SYNCHRONIZATION.
#
# - NOSTOP_WHEN_BELOW_MINTIMESTEP:
# If this is activated, the code will not terminate when the
# timestep falls below the value of MinSizeTimestep specified in
# the parameterfile. This is useful for runs where one wants to
# enforce a constant timestep for all particles. This can be done
# by activating this option, and by setting MinSizeTimestep and
# MaxSizeTimestep to an equal value.
#
# - NOPMSTEPADJUSTMENT:
# When this is set, the long-range timestep for the PM-force
# computation (when the TreePM algorithm is used) is always
# determined by MaxSizeTimeStep. Otherwise, it is determined by
# the MaxRMSDisplacement parameter, or MaxSizeTimeStep, whichever
# gives the smaller step.
#
# - HAVE_HDF5:
# If this is set, the code will be compiled with support for input
# and output in the HDF5 format. You need to have the HDF5
# libraries and headers installed on your computer for this option
# to work. The HDF5 format can then be selected as format "3" in
# Gadget's parameterfile.
#
# - OUTPUTPOTENTIAL:
# This will make the code compute gravitational potentials for
# all particles each time a snapshot file is generated. The values
# are then included in the snapshot file. Note that the computation
# of the values of the gravitational potential costs additional CPU.
#
# - OUTPUTACCELERATION:
# This will include the physical acceleration of each particle in
# snapshot files.
#
# - OUTPUTCHANGEOFENTROPY:
# This will include the rate of change of entropy of gas particles
# in snapshot files.
#
# - OUTPUTTIMESTEP:
# This will include the current timesteps of all particles in the
# snapshot files.
#
# - NOGRAVITY
# This switches off gravity. Useful only for pure SPH simulations
# in non-expanding space.
#
# - NOTREERND:
# If this is not set, the tree construction will succeed even when
# there are a few particles at identical locations. This is done by
# `rerouting' particles once the node-size has fallen below 1.0e-3
# of the softening length. When this option is activated, this will
# be surpressed and the tree construction will always fail if there
# are particles at extremely close coordinates.
#
# - NOTYPEPREFIX_FFTW:
# This is an option that signals that FFTW has been compiled
# without the type-prefix option, i.e. no leading "d" or "s"
# characters are used to access the library.
#
# - LONG_X/Y/Z:
# These options can be used together with PERIODIC and NOGRAVITY only.
# When set, the options define numerical factors that can be used to
# distorts the periodic simulation cube into a parallelepiped of
# arbitrary aspect ratio. This can be useful for idealized SPH tests.
#
# - TWODIMS:
# This effectively switches of one dimension in SPH, i.e. the code
# follows only 2d hydrodynamics in the xy-, yz-, or xz-plane. This
# only works with NOGRAVITY, and if all coordinates of the third
# axis are exactly equal. Can be useful for idealized SPH tests.
#
# - SPH_BND_PARTICLES:
# If this is set, particles with a particle-ID equal to zero do not
# receive any SPH acceleration. This can be useful for idealized
# SPH tests, where these particles represent fixed "walls".
#
# - NOVISCOSITYLIMITER:
# If this is set, the code will not try to put an upper limit on
# the viscous force in case an implausibly high pair-wise viscous
# force (which may lead to a particle 'reflection' in case of poor
# timestepping) should arise. Note: For proper settings of the
# timestep parameters, this situation should not arise.
#
# - COMPUTE_POTENTIAL_ENERGY:
# When this option is set, the code will compute the gravitational
# potential energy each time a global statistics is computed. This
# can be useful for testing global energy conservation.
#
# - LONGIDS:
# If this is set, the code assumes that particle-IDs are stored as
# 64-bit long integers. This is only really needed if you want to
# go beyond ~2 billion particles.
#
# - ISOTHERM_EQS:
# This special option makes the gas behave like an isothermal gas
# with equation of state P = cs^2 * rho. The sound-speed cs is set by
# the thermal energy per unit mass in the intial conditions,
# i.e. cs^2=u. If the value for u is zero, then the initial gas
# temperature in the parameter file is used to define the sound speed
# according to cs^2 = 3/2 kT/mp, where mp is the proton mass.
#
# - ADAPTIVE_GRAVSOFT_FORGAS:
# When this option is set, the gravitational softening lengths used for
# gas particles is tied to their SPH smoothing length. This can be useful
# for dissipative collapse simulations. The option requires the setting
# of UNEQUALSOFTENINGS.
#
# - SELECTIVE_NO_GRAVITY:
# This can be used for special computations where one wants to
# exclude certain particle types from receiving gravitational
# forces. The particle types that are excluded in this fashion are
# specified by a bit mask, in the same as for the PLACEHIGHRESREGION
# option.
#
# - FORCETEST:
# This can be set to check the force accuracy of the code. The
# option needs to be set to a number between 0 and 1 (e.g. 0.01),
# which is taken to specify a random fraction of particles for
# which at each timestep forces by direct summation are
# computed. The normal tree-forces and the correct direct
# summation forces are collected in a file. Note that the
# simulation itself is unaffected by this option, but it will of
# course run much(!) slower, especially if
# FORCETEST*NumPart*NumPart >> NumPart. Note: Particle IDs must
# be set to numbers >=1 for this to work.
#
# - MAKEGLASS
# This option can be used to generate a glass-like particle
# configuration. The value assigned gives the particle load,
# which is initially generated as a Poisson sample and then
# evolved towards a glass with the sign of gravity reversed.
#
#-----------------------------------------------------------------------
diff --git a/allvars.h b/allvars.h
index 052f698..621bb49 100644
--- a/allvars.h
+++ b/allvars.h
@@ -1,1675 +1,1682 @@
/*! \file allvars.h
* \brief declares global variables.
*
* 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's, typedef's, and enum's
* - add #include "allvars.h", delete the #ifndef ALLVARS_H conditional
* - delete all keywords 'extern'
* - delete all struct definitions enclosed in {...}, e.g.
* "extern struct global_data_all_processes {....} All;"
* becomes "struct global_data_all_processes All;"
*/
#ifndef ALLVARS_H
#define ALLVARS_H
#include <stdio.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_integration.h>
#include "tags.h"
#define GADGETVERSION "2.0" /*!< code version string */
#define TIMEBASE (1<<28) /*!< The simulated timespan is mapped onto the integer interval [0,TIMESPAN],
* where TIMESPAN needs to be a power of 2. Note that (1<<28) corresponds to 2^29
*/
#define MAXTOPNODES 200000 /*!< Maximum number of nodes in the top-level tree used for domain decomposition */
typedef long long peanokey; /*!< defines the variable type used for Peano-Hilbert keys */
#define BITS_PER_DIMENSION 18 /*!< Bits per dimension available for Peano-Hilbert order.
Note: If peanokey is defined as type int, the allowed maximum is 10.
If 64-bit integers are used, the maximum is 21 */
#define PEANOCELLS (((peanokey)1)<<(3*BITS_PER_DIMENSION)) /*!< The number of different Peano-Hilbert cells */
#define RNDTABLE 3000 /*!< gives the length of a table with random numbers, refreshed at every timestep.
This is used to allow application of random numbers to a specific particle
in a way that is independent of the number of processors used. */
#define MAX_REAL_NUMBER 1e37
#define MIN_REAL_NUMBER 1e-37
#define MAXLEN_FILENAME 100 /*!< Maximum number of characters for filenames (including the full path) */
#ifdef ISOTHERM_EQS
#define GAMMA (1.0) /*!< index for isothermal gas */
#else
#define GAMMA (5.0/3) /*!< adiabatic index of simulated gas */
#endif
#define GAMMA_MINUS1 (GAMMA-1)
#define HYDROGEN_MASSFRAC 0.76 /*!< mass fraction of hydrogen, relevant only for radiative cooling */
/* Some physical constants in cgs units */
#define GRAVITY 6.672e-8 /*!< Gravitational constant (in cgs units) */
#define SOLAR_MASS 1.989e33
#define SOLAR_LUM 3.826e33
#define RAD_CONST 7.565e-15
#define AVOGADRO 6.0222e23
#define BOLTZMANN 1.3806e-16
#define GAS_CONST 8.31425e7
#define C 2.9979e10
#define PLANCK 6.6262e-27
#define CM_PER_MPC 3.085678e24
#define PROTONMASS 1.6726e-24
#define ELECTRONMASS 9.10953e-28
#define THOMPSON 6.65245e-25
#define ELECTRONCHARGE 4.8032e-10
#define HUBBLE 3.2407789e-18 /* in h/sec */
#define YEAR_IN_SECOND 31536000.0 /* year in sec */
#define FEH_SOLAR 0.00181 /* used only if cooling with metal is on and chimie is off */
#define PI 3.1415926535897931
#define TWOPI 6.2831853071795862
/* Some conversion factors */
#define SEC_PER_MEGAYEAR 3.155e13
#define SEC_PER_YEAR 3.155e7
#ifndef ASMTH
#define ASMTH 1.25 /*!< ASMTH gives the scale of the short-range/long-range force split in units of FFT-mesh cells */
#endif
#ifndef RCUT
#define RCUT 4.5 /*!< RCUT gives the maximum distance (in units of the scale used for the force split) out to
which short-range forces are evaluated in the short-range tree walk. */
#endif
#define MAX_NGB 20000 /*!< defines maximum length of neighbour list */
#define MAXLEN_OUTPUTLIST 500 /*!< maxmimum number of entries in list of snapshot output times */
#define DRIFT_TABLE_LENGTH 1000 /*!< length of the lookup table used to hold the drift and kick factors */
#ifdef COSMICTIME
#define COSMICTIME_TABLE_LENGTH 1000 /*!< length of the lookup table used for the cosmic time computation */
#endif
#define MAXITER 1000 /*!< maxmimum number of steps for SPH neighbour iteration */
#ifdef DOUBLEPRECISION /*!< If defined, the variable type FLOAT is set to "double", otherwise to FLOAT */
#define FLOAT double
#else
#define FLOAT float
#endif
#ifndef TWODIMS
#define NUMDIMS 3 /*!< For 3D-normalized kernel */
#define KERNEL_COEFF_1 2.546479089470 /*!< Coefficients for SPH spline kernel and its derivative */
#define KERNEL_COEFF_2 15.278874536822
#define KERNEL_COEFF_3 45.836623610466
#define KERNEL_COEFF_4 30.557749073644
#define KERNEL_COEFF_5 5.092958178941
#define KERNEL_COEFF_6 (-15.278874536822)
#define NORM_COEFF 4.188790204786 /*!< Coefficient for kernel normalization. Note: 4.0/3 * PI = 4.188790204786 */
#else
#define NUMDIMS 2 /*!< For 2D-normalized kernel */
#define KERNEL_COEFF_1 (5.0/7*2.546479089470) /*!< Coefficients for SPH spline kernel and its derivative */
#define KERNEL_COEFF_2 (5.0/7*15.278874536822)
#define KERNEL_COEFF_3 (5.0/7*45.836623610466)
#define KERNEL_COEFF_4 (5.0/7*30.557749073644)
#define KERNEL_COEFF_5 (5.0/7*5.092958178941)
#define KERNEL_COEFF_6 (5.0/7*(-15.278874536822))
#define NORM_COEFF M_PI /*!< Coefficient for kernel normalization. */
#endif
#ifdef MULTIPHASE
#define GAS_SPH 0
#define GAS_STICKY 1
#define GAS_DARK 2
#endif
#if defined(SFR) || defined(STELLAR_PROP)
#define ST 1
#endif
#ifdef CHIMIE
#define NELEMENTS 5
#define MAXNELEMENTS 64
#define FIRST_ELEMENT "Fe"
#define FE 0
#endif
#ifdef COOLING
#define COOLING_NMETALICITIES 9
#define COOLING_NTEMPERATURES 171
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
#define VELOCITY_DISPERSION_SIZE 3
#endif
extern int SetMinTimeStepForActives;
extern int ThisTask; /*!< the rank of the local processor */
extern int NTask; /*!< number of processors */
extern int PTask; /*!< smallest integer such that NTask <= 2^PTask */
extern int NumPart; /*!< number of particles on the LOCAL processor */
extern int N_gas; /*!< number of gas particles on the LOCAL processor */
#if defined(SFR) || defined(STELLAR_PROP)
extern int N_stars; /*!< number of stars particle on the LOCAL processor */
#endif
#ifdef MULTIPHASE
extern int N_sph;
extern int N_sticky;
extern int N_stickyflaged;
extern int N_dark;
extern int NumColPotLocal; /*!< local number of potentially collisional particles */
extern int NumColPot; /*!< total number of potentially collisional particles */
extern int NumColLocal; /*!< local number of collisions */
extern int NumCol; /*!< total number of collisions */
extern int NumNoColLocal;
extern int NumNoCol;
#endif
extern long long Ntype[6]; /*!< total number of particles of each type */
extern int NtypeLocal[6]; /*!< local number of particles of each type */
extern int NumForceUpdate; /*!< number of active particles on local processor in current timestep */
extern int NumSphUpdate; /*!< number of active SPH particles on local processor in current timestep */
#ifdef CHIMIE
extern int NumStUpdate;
#endif
extern double CPUThisRun; /*!< Sums the CPU time for the process (current submission only) */
#ifdef SPLIT_DOMAIN_USING_TIME
extern double CPU_Gravity;
#endif
extern int RestartFlag; /*!< taken from command line used to start code. 0 is normal start-up from
initial conditions, 1 is resuming a run from a set of restart files, while 2
marks a restart from a snapshot file. */
extern char *Exportflag; /*!< Buffer used for flagging whether a particle needs to be exported to another process */
extern int *Ngblist; /*!< Buffer to hold indices of neighbours retrieved by the neighbour search routines */
extern int TreeReconstructFlag; /*!< Signals that a new tree needs to be constructed */
#ifdef SFR
extern int RearrangeParticlesFlag;/*!< Signals that particles must be rearanged */
#endif
extern int Flag_FullStep; /*!< This flag signals that the current step involves all particles */
extern gsl_rng *random_generator; /*!< the employed random number generator of the GSL library */
extern double RndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */
#ifdef SFR
extern double StarFormationRndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */
#endif
#ifdef FEEDBACK_WIND
extern double FeedbackWindRndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */
#endif
#ifdef CHIMIE
extern double ChimieRndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
extern double ChimieKineticFeedbackRndTable[RNDTABLE]; /*!< Hold a table with random numbers, refreshed every timestep */
#endif
extern double DomainCorner[3]; /*!< gives the lower left corner of simulation volume */
extern double DomainCenter[3]; /*!< gives the center of simulation volume */
extern double DomainLen; /*!< gives the (maximum) side-length of simulation volume */
extern double DomainFac; /*!< factor used for converting particle coordinates to a Peano-Hilbert mesh covering the simulation volume */
extern int DomainMyStart; /*!< first domain mesh cell that resides on the local processor */
extern int DomainMyLast; /*!< last domain mesh cell that resides on the local processor */
extern int *DomainStartList; /*!< a table that lists the first domain mesh cell for all processors */
extern int *DomainEndList; /*!< a table that lists the last domain mesh cell for all processors */
extern double *DomainWork; /*!< a table that gives the total "work" due to the particles stored by each processor */
extern int *DomainCount; /*!< a table that gives the total number of particles held by each processor */
extern int *DomainCountSph; /*!< a table that gives the total number of SPH particles held by each processor */
extern int *DomainTask; /*!< this table gives for each leaf of the top-level tree the processor it was assigned to */
extern int *DomainNodeIndex; /*!< this table gives for each leaf of the top-level tree the corresponding node of the gravitational tree */
extern FLOAT *DomainTreeNodeLen; /*!< this table gives for each leaf of the top-level tree the side-length of the corresponding node of the gravitational tree */
extern FLOAT *DomainHmax; /*!< this table gives for each leaf of the top-level tree the maximum SPH smoothing length among the particles of the corresponding node of the gravitational tree */
extern struct DomainNODE
{
FLOAT s[3]; /*!< center-of-mass coordinates */
FLOAT vs[3]; /*!< center-of-mass velocities */
FLOAT mass; /*!< mass of node */
#ifdef STELLAR_FLUX
FLOAT starlum; /*!< star luminosity of node */
#endif
#ifdef UNEQUALSOFTENINGS
#ifndef ADAPTIVE_GRAVSOFT_FORGAS
int bitflags; /*!< this bit-field encodes the particle type with the largest softening among the particles of the nodes, and whether there are particles with different softening in the node */
#else
FLOAT maxsoft; /*!< hold the maximum gravitational softening of particles in the
node if the ADAPTIVE_GRAVSOFT_FORGAS option is selected */
#endif
#endif
}
*DomainMoment; /*!< this table stores for each node of the top-level tree corresponding node data from the gravitational tree */
extern peanokey *DomainKeyBuf; /*!< this points to a buffer used during the exchange of particle data */
extern peanokey *Key; /*!< a table used for storing Peano-Hilbert keys for particles */
extern peanokey *KeySorted; /*!< holds a sorted table of Peano-Hilbert keys for all particles, used to construct top-level tree */
extern int NTopnodes; /*!< total number of nodes in top-level tree */
extern int NTopleaves; /*!< number of leaves in top-level tree. Each leaf can be assigned to a different processor */
extern struct topnode_data
{
int Daughter; /*!< index of first daughter cell (out of 8) of top-level node */
int Pstart; /*!< for the present top-level node, this gives the index of the first node in the concatenated list of topnodes collected from all processors */
int Blocks; /*!< for the present top-level node, this gives the number of corresponding nodes in the concatenated list of topnodes collected from all processors */
int Leaf; /*!< if the node is a leaf, this gives its number when all leaves are traversed in Peano-Hilbert order */
peanokey Size; /*!< number of Peano-Hilbert mesh-cells represented by top-level node */
peanokey StartKey; /*!< first Peano-Hilbert key in top-level node */
long long Count; /*!< counts the number of particles in this top-level node */
}
*TopNodes; /*!< points to the root node of the top-level tree */
extern double TimeOfLastTreeConstruction; /*!< holds what it says, only used in connection with FORCETEST */
/* variables for input/output, usually only used on process 0 */
extern char ParameterFile[MAXLEN_FILENAME]; /*!< file name of parameterfile used for starting the simulation */
extern FILE *FdInfo; /*!< file handle for info.txt log-file. */
extern FILE *FdLog; /*!< file handle for log.txt log-file. */
extern FILE *FdEnergy; /*!< file handle for energy.txt log-file. */
#ifdef SYSTEMSTATISTICS
extern FILE *FdSystem;
#endif
extern FILE *FdTimings; /*!< file handle for timings.txt log-file. */
extern FILE *FdCPU; /*!< file handle for cpu.txt log-file. */
#ifdef FORCETEST
extern FILE *FdForceTest; /*!< file handle for forcetest.txt log-file. */
#endif
#ifdef SFR
extern FILE *FdSfr; /*!< file handle for sfr.txt log-file. */
#endif
#ifdef CHIMIE
extern FILE *FdChimie; /*!< file handle for chimie log-file. */
#endif
#ifdef MULTIPHASE
extern FILE *FdPhase; /*!< file handle for pase.txt log-file. */
extern FILE *FdSticky; /*!< file handle for sticky.txt log-file. */
#endif
#ifdef AGN_ACCRETION
extern FILE *FdAccretion; /*!< file handle for accretion.txt log-file. */
#endif
#ifdef BONDI_ACCRETION
extern FILE *FdBondi; /*!< file handle for bondi.txt log-file. */
#endif
#ifdef BUBBLES
extern FILE *FdBubble; /*!< file handle for bubble.txt log-file. */
#endif
extern double DriftTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological drift factors */
extern double GravKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for gravitational forces */
extern double HydroKickTable[DRIFT_TABLE_LENGTH]; /*!< table for the cosmological kick factor for hydrodynmical forces */
#ifdef COSMICTIME
extern double CosmicTimeTable[COSMICTIME_TABLE_LENGTH]; /*!< table for the computation of cosmic time */
#endif
extern void *CommBuffer; /*!< points to communication buffer, which is used in the domain decomposition, the
parallel tree-force computation, the SPH routines, etc. */
/*! 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_all_processes
{
long long TotNumPart; /*!< total particle numbers (global value) */
long long TotN_gas; /*!< total gas particle number (global value) */
#if defined(SFR) || defined(STELLAR_PROP)
long long TotN_stars; /*!< total stars particle number (global value) */
#endif
#ifdef MULTIPHASE
long long TotN_sph; /*!< total sph particle number (global value) */
long long TotN_sticky; /*!< total sticky particle number (global value) */
long long TotN_stickyflaged; /*!< total sticky flaged particle number (global value) */
long long TotN_stickyactive; /*!< total sticky active particle number (global value) */
long long TotN_dark; /*!< total dark particle number (global value) */
#endif
int MaxPart; /*!< This gives the maxmimum number of particles that can be stored on one processor. */
int MaxPartSph; /*!< This gives the maxmimum number of SPH particles that can be stored on one processor. */
#ifdef STELLAR_PROP
int MaxPartStars; /*!< This gives the maxmimum number of Star particles that can be stored on one processor. */
#endif
double BoxSize; /*!< Boxsize in case periodic boundary conditions are used */
int ICFormat; /*!< selects different versions of IC file-format */
int SnapFormat; /*!< selects different versions of snapshot file-formats */
int NumFilesPerSnapshot; /*!< number of files in multi-file snapshot dumps */
int NumFilesWrittenInParallel;/*!< maximum number of files that may be written simultaneously when
writing/reading restart-files, or when writing snapshot files */
int BufferSize; /*!< size of communication buffer in MB */
int BunchSizeForce; /*!< number of particles fitting into the buffer in the parallel tree-force algorithm */
int BunchSizeDensity; /*!< number of particles fitting into the communication buffer in the density computation */
int BunchSizeHydro; /*!< number of particles fitting into the communication buffer in the SPH hydrodynamical force computation */
int BunchSizeDomain; /*!< number of particles fitting into the communication buffer in the domain decomposition */
#ifdef MULTIPHASE
int BunchSizeSticky; /*!< number of particles fitting into the communication buffer in the Chimie computation */
#endif
#ifdef CHIMIE
int BunchSizeChimie; /*!< number of particles fitting into the communication buffer in the Chimie computation */
int BunchSizeStarsDensity; /*!< number of particles fitting into the communication buffer in the star density computation */
#endif
double PartAllocFactor; /*!< in order to maintain work-load balance, the particle load will usually
NOT be balanced. Each processor allocates memory for PartAllocFactor times
the average number of particles to allow for that */
double TreeAllocFactor; /*!< Each processor allocates a number of nodes which is TreeAllocFactor times
the maximum(!) number of particles. Note: A typical local tree for N
particles needs usually about ~0.65*N nodes. */
#ifdef SFR
double StarsAllocFactor; /*!< Estimated fraction of gas particles that will form stars during the simulation
This allow to reduce the memory stored for stellar particles */
#endif
/* some SPH parameters */
double DesNumNgb; /*!< Desired number of SPH neighbours */
double MaxNumNgbDeviation; /*!< Maximum allowed deviation neighbour number */
double ArtBulkViscConst; /*!< Sets the parameter \f$\alpha\f$ of the artificial viscosity */
#ifdef ART_CONDUCTIVITY
double ArtCondConst; /*!< Sets the parameter \f$\alpha\f$ of the artificial conductivity */
double ArtCondThreshold;
#endif
double InitGasTemp; /*!< may be used to set the temperature in the IC's */
double MinGasTemp; /*!< may be used to set a floor for the gas temperature */
double MinEgySpec; /*!< the minimum allowed temperature expressed as energy per unit mass */
/* Usefull constants */
double Boltzmann;
double ProtonMass;
double mumh;
#ifdef COOLING
/* Cooling parameters */
double *logT;
double *logL;
gsl_interp_accel *acc_cooling_spline;
gsl_spline *cooling_spline;
double CoolingType;
char CoolingFile[MAXLEN_FILENAME]; /*!< cooling file */
double CutofCoolingTemperature;
double InitGasMetallicity;
/*
new metal dependent cooling
*/
double CoolingParameters_zmin;
double CoolingParameters_zmax;
double CoolingParameters_slz;
double CoolingParameters_tmin;
double CoolingParameters_tmax;
double CoolingParameters_slt;
double CoolingParameters_FeHSolar;
double CoolingParameters_cooling_data_max;
double CoolingParameters_cooling_data[COOLING_NMETALICITIES][COOLING_NTEMPERATURES];
int CoolingParameters_p;
int CoolingParameters_q;
#endif
#ifdef CHIMIE
int ChimieNumberOfParameterFiles;
char ChimieParameterFile[MAXLEN_FILENAME]; /*!< chimie parameter file */
double ChimieSupernovaEnergy;
double ChimieKineticFeedbackFraction;
double ChimieWindSpeed;
double ChimieWindTime;
double ChimieSNIaThermalTime;
double ChimieSNIIThermalTime;
double ChimieMaxSizeTimestep;
#endif
#if !defined (HEATING_PE)
double HeatingPeElectronFraction;
#endif
#if !defined (HEATING_PE) || defined (STELLAR_FLUX) || defined (EXTERNAL_FLUX)
double HeatingPeSolarEnergyDensity;
#endif
#if !defined (HEATING_PE) || defined (STELLAR_FLUX)
double HeatingPeLMRatioGas;
double HeatingPeLMRatioHalo;
double HeatingPeLMRatioDisk;
double HeatingPeLMRatioBulge;
double HeatingPeLMRatioStars;
double HeatingPeLMRatioBndry;
double HeatingPeLMRatio[6];
#endif
#ifdef EXTERNAL_FLUX
double HeatingExternalFLuxEnergyDensity;
#endif
#ifdef MULTIPHASE
double CriticalTemperature;
double CriticalEgySpec;
double CriticalNonCollisionalTemperature;
double CriticalNonCollisionalEgySpec;
#ifdef COLDGAS_CYCLE
double ColdGasCycleTransitionTime;
double ColdGasCycleTransitionParameter;
#endif
#endif
#ifdef MULTIPHASE
/* some STICKY parameters */
int StickyUseGridForCollisions;
double StickyTime; /*!< Cooling time of sticky particle collision */
double StickyCollisionTime;
double StickyLastCollisionTime;
double StickyIdleTime;
double StickyMinVelocity;
double StickyMaxVelocity;
int StickyGridNx;
int StickyGridNy;
int StickyGridNz;
double StickyGridXmin;
double StickyGridXmax;
double StickyGridYmin;
double StickyGridYmax;
double StickyGridZmin;
double StickyGridZmax;
double StickyLambda;
double StickyDensity;
double StickyDensityPower;
double StickyBetaR;
double StickyBetaT;
double StickyRsphFact; /*!< Fraction of the sph radius used in sticky particle */
#endif
#ifdef OUTERPOTENTIAL
#ifdef NFW
double HaloConcentration;
double HaloMass;
double GasMassFraction;
double NFWPotentialCte;
double Rs;
#endif
#ifdef PLUMMER
double PlummerMass;
double PlummerSoftenning;
double PlummerPotentialCte;
#endif
#ifdef MIYAMOTONAGAI
double MiyamotoNagaiMass;
double MiyamotoNagaiHr;
double MiyamotoNagaiHz;
double MiyamotoNagaiPotentialCte;
#endif
#ifdef PISOTHERM
double Rho0;
double Rc;
double PisothermPotentialCte;
double GasMassFraction;
double PotentialInf;
gsl_function PotentialF;
gsl_integration_workspace *Potentialw;
#endif
#ifdef CORIOLIS
double CoriolisOmegaX;
double CoriolisOmegaY;
double CoriolisOmegaZ;
double CoriolisOmegaX0;
double CoriolisOmegaY0;
double CoriolisOmegaZ0;
#endif
#endif
#ifdef SFR
int StarFormationNStarsFromGas;
double StarFormationStarMass;
double StarFormationMgMsFraction;
int StarFormationType;
double StarFormationCstar;
double StarFormationTime;
double StarFormationDensity;
double StarFormationTemperature;
double ThresholdDensity;
#endif
#ifdef FEEDBACK
double SupernovaTime;
#endif
#ifdef FEEDBACK_WIND
double SupernovaWindEgySpecPerMassUnit;
double SupernovaWindFractionInEgyKin;
double SupernovaWindParameter;
double SupernovaWindSpeed;
double SupernovaWindIntAccuracy;
#endif
#ifdef AGN_ACCRETION
double TimeBetAccretion;
double AccretionRadius;
double AGNFactor;
double MinMTotInRa;
double TimeLastAccretion;
double LastMTotInRa;
double MTotInRa;
double dMTotInRa;
#endif
#ifdef BUBBLES
char BubblesInitFile[MAXLEN_FILENAME]; /*!< bubble file */
double *BubblesTime;
double *BubblesD;
double *BubblesR;
double *BubblesE;
double *BubblesA;
double *BubblesB;
int BubblesIndex;
double BubblesAlpha;
double BubblesBeta;
double BubblesDelta;
double BubblesRadiusFactor;
double EnergyBubbles;
#endif
#ifdef AGN_HEATING
double AGNHeatingPower;
double AGNHeatingRmax;
#endif
#ifdef BONDI_ACCRETION
double BondiEfficiency;
double BondiBlackHoleMass;
double BondiHsmlFactor;
double BondiPower;
double BondiTimeBet;
double BondiTimeLast;
#endif
#if defined (AGN_ACCRETION) || defined (BONDI_ACCRETION)
double LightSpeed;
#endif
/* some force counters */
long long TotNumOfForces; /*!< counts total number of force computations */
long long NumForcesSinceLastDomainDecomp; /*!< count particle updates since last domain decomposition */
/* system of units */
double G; /*!< Gravity-constant in internal units */
double UnitTime_in_s; /*!< factor to convert internal time unit to seconds/h */
double UnitMass_in_g; /*!< factor to convert internal mass unit to grams/h */
double UnitVelocity_in_cm_per_s; /*!< factor to convert intqernal velocity unit to cm/sec */
double UnitLength_in_cm; /*!< factor to convert internal length unit to cm/h */
double UnitPressure_in_cgs; /*!< factor to convert internal pressure unit to cgs units (little 'h' still around!) */
double UnitDensity_in_cgs; /*!< factor to convert internal length unit to g/cm^3*h^2 */
double UnitCoolingRate_in_cgs; /*!< factor to convert internal cooling rate to cgs units */
double UnitEnergy_in_cgs; /*!< factor to convert internal energy to cgs units */
double UnitTime_in_Megayears; /*!< factor to convert internal time to megayears/h */
double GravityConstantInternal; /*!< If set to zero in the parameterfile, the internal value of the
gravitational constant is set to the Newtonian value based on the system of
units specified. Otherwise the value provided is taken as internal gravity constant G. */
/* Cosmological parameters */
double Hubble; /*!< Hubble-constant in internal units */
double Omega0; /*!< matter density in units of the critical density (at z=0)*/
double OmegaLambda; /*!< vaccum energy density relative to crictical density (at z=0) */
double OmegaBaryon; /*!< baryon density in units of the critical density (at z=0)*/
double HubbleParam; /*!< little `h', i.e. Hubble constant in units of 100 km/s/Mpc. Only needed to get absolute physical values for cooling physics */
/* Code options */
int ComovingIntegrationOn; /*!< flags that comoving integration is enabled */
int PeriodicBoundariesOn; /*!< flags that periodic boundaries are enabled */
int ResubmitOn; /*!< flags that automatic resubmission of job to queue system is enabled */
int TypeOfOpeningCriterion; /*!< determines tree cell-opening criterion: 0 for Barnes-Hut, 1 for relative criterion */
int TypeOfTimestepCriterion; /*!< gives type of timestep criterion (only 0 supported right now - unlike gadget-1.1) */
int OutputListOn; /*!< flags that output times are listed in a specified file */
/* Parameters determining output frequency */
int SnapshotFileCount; /*!< number of snapshot that is written next */
double TimeBetSnapshot; /*!< simulation time interval between snapshot files */
double TimeOfFirstSnapshot; /*!< simulation time of first snapshot files */
double CpuTimeBetRestartFile; /*!< cpu-time between regularly generated restart files */
double TimeLastRestartFile; /*!< cpu-time when last restart-file was written */
double TimeBetStatistics; /*!< simulation time interval between computations of energy statistics */
double TimeLastStatistics; /*!< simulation time when the energy statistics was computed the last time */
int NumCurrentTiStep; /*!< counts the number of system steps taken up to this point */
/* Current time of the simulation, global step, and end of simulation */
double Time; /*!< current time of the simulation */
double TimeBegin; /*!< time of initial conditions of the simulation */
double TimeStep; /*!< difference between current times of previous and current timestep */
double TimeMax; /*!< marks the point of time until the simulation is to be evolved */
/* variables for organizing discrete timeline */
double Timebase_interval; /*!< factor to convert from floating point time interval to integer timeline */
int Ti_Current; /*!< current time on integer timeline */
int Ti_nextoutput; /*!< next output time on integer timeline */
#ifdef FLEXSTEPS
int PresentMinStep; /*!< If FLEXSTEPS is used, particle timesteps are chosen as multiples of the present minimum timestep. */
int PresentMaxStep; /*!< If FLEXSTEPS is used, this is the maximum timestep in timeline units, rounded down to the next power 2 division */
#endif
#ifdef PMGRID
int PM_Ti_endstep; /*!< begin of present long-range timestep */
int PM_Ti_begstep; /*!< end of present long-range timestep */
#endif
/* Placement of PM grids */
#ifdef PMGRID
double Asmth[2]; /*!< Gives the scale of the long-range/short-range split (in mesh-cells), both for the coarse and the high-res mesh */
double Rcut[2]; /*!< Gives the maximum radius for which the short-range force is evaluated with the tree (in mesh-cells), both for the coarse and the high-res mesh */
double Corner[2][3]; /*!< lower left corner of coarse and high-res PM-mesh */
double UpperCorner[2][3]; /*!< upper right corner of coarse and high-res PM-mesh */
double Xmintot[2][3]; /*!< minimum particle coordinates both for coarse and high-res PM-mesh */
double Xmaxtot[2][3]; /*!< maximum particle coordinates both for coarse and high-res PM-mesh */
double TotalMeshSize[2]; /*!< total extension of coarse and high-res PM-mesh */
#endif
/* Variables that keep track of cumulative CPU consumption */
double TimeLimitCPU; /*!< CPU time limit as defined in parameterfile */
double CPU_TreeConstruction; /*!< time spent for constructing the gravitational tree */
double CPU_TreeWalk; /*!< actual time spent for pure tree-walks */
double CPU_Gravity; /*!< cumulative time used for gravity computation (tree-algorithm only) */
double CPU_Potential; /*!< time used for computing gravitational potentials */
double CPU_Domain; /*!< cumulative time spent for domain decomposition */
double CPU_Snapshot; /*!< time used for writing snapshot files */
double CPU_Total; /*!< cumulative time spent for domain decomposition */
double CPU_CommSum; /*!< accumulated time used for communication, and for collecting partial results, in tree-gravity */
double CPU_Imbalance; /*!< cumulative time lost accross all processors as work-load imbalance in gravitational tree */
double CPU_HydCompWalk; /*!< time used for actual SPH computations, including neighbour search */
double CPU_HydCommSumm; /*!< cumulative time used for communication in SPH, and for collecting partial results */
double CPU_HydImbalance; /*!< cumulative time lost due to work-load imbalance in SPH */
double CPU_Hydro; /*!< cumulative time spent for SPH related computations */
#ifdef SFR
double CPU_StarFormation; /*!< cumulative time spent for star formation computations */
#endif
#ifdef CHIMIE
double CPU_Chimie; /*!< cumulative time spent for chimie computations */
+ double CPU_ChimieDensCompWalk;
+ double CPU_ChimieDensCommSumm;
+ double CPU_ChimieDensImbalance;
+ double CPU_ChimieDensEnsureNgb;
+ double CPU_ChimieCompWalk;
+ double CPU_ChimieCommSumm;
+ double CPU_ChimieImbalance;
#endif
#ifdef MULTIPHASE
double CPU_Sticky; /*!< cumulative time spent for sticky computations */
#endif
double CPU_EnsureNgb; /*!< time needed to iterate on correct neighbour numbers */
double CPU_Predict; /*!< cumulative time to drift the system forward in time, including dynamic tree updates */
double CPU_TimeLine; /*!< time used for determining new timesteps, and for organizing the timestepping, including kicks of active particles */
double CPU_PM; /*!< time used for long-range gravitational force */
double CPU_Peano; /*!< time required to establish Peano-Hilbert order */
#ifdef DETAILED_CPU_DOMAIN
double CPU_Domain_findExtend;
double CPU_Domain_determineTopTree;
double CPU_Domain_sumCost;
double CPU_Domain_findSplit;
double CPU_Domain_shiftSplit;
double CPU_Domain_countToGo;
double CPU_Domain_exchange;
#endif
#ifdef DETAILED_CPU_GRAVITY
double CPU_Gravity_TreeWalk1;
double CPU_Gravity_TreeWalk2;
double CPU_Gravity_CommSum1;
double CPU_Gravity_CommSum2;
double CPU_Gravity_Imbalance1;
double CPU_Gravity_Imbalance2;
#endif
#ifdef COOLING
double CPU_Cooling;
#endif
#ifdef DETAILED_CPU
double CPU_Leapfrog;
double CPU_Physics;
double CPU_Residual;
double CPU_Accel;
double CPU_Begrun;
#endif
/* tree code opening criterion */
double ErrTolTheta; /*!< BH tree opening angle */
double ErrTolForceAcc; /*!< parameter for relative opening criterion in tree walk */
/* adjusts accuracy of time-integration */
double ErrTolIntAccuracy; /*!< accuracy tolerance parameter \f$ \eta \f$ for timestep criterion. The
timestep is \f$ \Delta t = \sqrt{\frac{2 \eta eps}{a}} \f$ */
double MinSizeTimestep; /*!< minimum allowed timestep. Normally, the simulation terminates if the
timestep determined by the timestep criteria falls below this limit. */
double MaxSizeTimestep; /*!< maximum allowed timestep */
double MaxRMSDisplacementFac; /*!< this determines a global timestep criterion for cosmological simulations
in comoving coordinates. To this end, the code computes the rms velocity
of all particles, and limits the timestep such that the rms displacement
is a fraction of the mean particle separation (determined from the
particle mass and the cosmological parameters). This parameter specifies
this fraction. */
double CourantFac; /*!< SPH-Courant factor */
/* frequency of tree reconstruction/domain decomposition */
double TreeDomainUpdateFrequency; /*!< controls frequency of domain decompositions */
/* Gravitational and hydrodynamical softening lengths (given in terms of an `equivalent' Plummer softening length).
* Five groups of particles are supported 0="gas", 1="halo", 2="disk", 3="bulge", 4="stars", 5="bndry"
*/
double MinGasHsmlFractional; /*!< minimum allowed SPH smoothing length in units of SPH gravitational softening length */
double MinGasHsml; /*!< minimum allowed SPH smoothing length */
double SofteningGas; /*!< comoving gravitational softening lengths for type 0 */
double SofteningHalo; /*!< comoving gravitational softening lengths for type 1 */
double SofteningDisk; /*!< comoving gravitational softening lengths for type 2 */
double SofteningBulge; /*!< comoving gravitational softening lengths for type 3 */
double SofteningStars; /*!< comoving gravitational softening lengths for type 4 */
double SofteningBndry; /*!< comoving gravitational softening lengths for type 5 */
double SofteningGasMaxPhys; /*!< maximum physical softening length for type 0 */
double SofteningHaloMaxPhys; /*!< maximum physical softening length for type 1 */
double SofteningDiskMaxPhys; /*!< maximum physical softening length for type 2 */
double SofteningBulgeMaxPhys; /*!< maximum physical softening length for type 3 */
double SofteningStarsMaxPhys; /*!< maximum physical softening length for type 4 */
double SofteningBndryMaxPhys; /*!< maximum physical softening length for type 5 */
double SofteningTable[6]; /*!< current (comoving) gravitational softening lengths for each particle type */
double ForceSoftening[6]; /*!< the same, but multiplied by a factor 2.8 - at that scale the force is Newtonian */
double MassTable[6]; /*!< Table with particle masses for particle types with equal mass.
If particle masses are all equal for one type, the corresponding entry in MassTable
is set to this value, allowing the size of the snapshot files to be reduced. */
/* some filenames */
char InitCondFile[MAXLEN_FILENAME]; /*!< filename of initial conditions */
char OutputDir[MAXLEN_FILENAME]; /*!< output directory of the code */
char SnapshotFileBase[MAXLEN_FILENAME]; /*!< basename to construct the names of snapshotf files */
char EnergyFile[MAXLEN_FILENAME]; /*!< name of file with energy statistics */
#ifdef SYSTEMSTATISTICS
char SystemFile[MAXLEN_FILENAME];
#endif
char CpuFile[MAXLEN_FILENAME]; /*!< name of file with cpu-time statistics */
char InfoFile[MAXLEN_FILENAME]; /*!< name of log-file with a list of the timesteps taken */
char LogFile[MAXLEN_FILENAME]; /*!< name of log-file with varied info */
#ifdef SFR
char SfrFile[MAXLEN_FILENAME]; /*!< name of file with sfr records */
#endif
#ifdef CHIMIE
char ChimieFile[MAXLEN_FILENAME]; /*!< name of file with chimie records */
#endif
#ifdef MULTIPHASE
char PhaseFile[MAXLEN_FILENAME]; /*!< name of file with phase records */
char StickyFile[MAXLEN_FILENAME]; /*!< name of file with sticky records */
#endif
#ifdef AGN_ACCRETION
char AccretionFile[MAXLEN_FILENAME]; /*!< name of file with accretion records */
#endif
#ifdef BONDI_ACCRETION
char BondiFile[MAXLEN_FILENAME]; /*!< name of file with bondi records */
#endif
#ifdef BUBBLES
char BubbleFile[MAXLEN_FILENAME]; /*!< name of file with bubble records */
#endif
char TimingsFile[MAXLEN_FILENAME]; /*!< name of file with performance metrics of gravitational tree algorithm */
char RestartFile[MAXLEN_FILENAME]; /*!< basename of restart-files */
char ResubmitCommand[MAXLEN_FILENAME]; /*!< name of script-file that will be executed for automatic restart */
char OutputListFilename[MAXLEN_FILENAME]; /*!< name of file with list of desired output times */
double OutputListTimes[MAXLEN_OUTPUTLIST]; /*!< table with desired output times */
int OutputListLength; /*!< number of output times stored in the table of desired output times */
#ifdef RANDOMSEED_AS_PARAMETER
int RandomSeed; /*!< initial random seed >*/
#endif
}
All; /*!< a container variable for global variables that are equal on all processors */
/*! This structure holds all the information that is
* stored for each particle of the simulation.
*/
extern struct particle_data
{
FLOAT Pos[3]; /*!< particle position at its current time */
FLOAT Mass; /*!< particle mass */
FLOAT Vel[3]; /*!< particle velocity at its current time */
FLOAT GravAccel[3]; /*!< particle acceleration due to gravity */
#ifdef PMGRID
FLOAT GravPM[3]; /*!< particle acceleration due to long-range PM gravity force*/
#endif
#ifdef FORCETEST
FLOAT GravAccelDirect[3]; /*!< particle acceleration when computed with direct summation */
#endif
FLOAT Potential; /*!< gravitational potential */
FLOAT OldAcc; /*!< magnitude of old gravitational force. Used in relative opening criterion */
#ifndef LONGIDS
unsigned int ID; /*!< particle identifier */
#else
unsigned long long ID; /*!< particle identifier */
#endif
int Type; /*!< flags particle type. 0=gas, 1=halo, 2=disk, 3=bulge, 4=stars, 5=bndry */
int Ti_endstep; /*!< marks start of current timestep of particle on integer timeline */
int Ti_begstep; /*!< marks end of current timestep of particle on integer timeline */
#ifdef FLEXSTEPS
int FlexStepGrp; /*!< a random 'offset' on the timeline to create a smooth groouping of particles */
#endif
float GravCost; /*!< weight factor used for balancing the work-load */
#ifdef PSEUDOSYMMETRIC
float AphysOld; /*!< magnitude of acceleration in last timestep. Used to make a first order
prediction of the change of acceleration expected in the future, thereby
allowing to guess whether a decrease/increase of the timestep should occur
in the timestep that is started. */
#endif
#ifdef PARTICLE_FLAG
float Flag;
#endif
#ifdef STELLAR_PROP
unsigned int StPIdx; /*!< index to the corresponding StP particle */
#endif
}
*P, /*!< holds particle data on local processor */
*DomainPartBuf; /*!< buffer for particle data used in domain decomposition */
/* the following struture holds data that is stored for each SPH particle in addition to the collisionless
* variables.
*/
extern struct sph_particle_data
{
FLOAT Entropy; /*!< current value of entropy (actually entropic function) of particle */
FLOAT Density; /*!< current baryonic mass density of particle */
FLOAT Hsml; /*!< current smoothing length */
FLOAT Left; /*!< lower bound in iterative smoothing length search */
FLOAT Right; /*!< upper bound in iterative smoothing length search */
FLOAT NumNgb; /*!< weighted number of neighbours found */
#ifdef AVOIDNUMNGBPROBLEM
FLOAT OldNumNgb;
#endif
FLOAT Pressure; /*!< current pressure */
FLOAT DtEntropy; /*!< rate of change of entropy */
#ifdef STELLAR_FLUX
FLOAT EnergyFlux; /*!< current value of local energy flux - Sph particles */
#endif
#ifdef AGN_HEATING
FLOAT EgySpecAGNHeat; /*!< current value of specific energy radiated of particle - Sph particles */
FLOAT DtEgySpecAGNHeat; /*!< rate of change of specific radiated energy - Sph particles */
FLOAT DtEntropyAGNHeat;
#endif
#ifdef MULTIPHASE
FLOAT StickyTime;
int StickyFlag;
#ifdef COUNT_COLLISIONS
float StickyCollisionNumber;
#endif
#endif
#ifdef FEEDBACK
FLOAT EgySpecFeedback;
FLOAT DtEgySpecFeedback;
FLOAT EnergySN;
FLOAT EnergySNrem;
FLOAT TimeSN;
FLOAT FeedbackVel[3]; /*!< kick due to feedback force */
#endif
#ifdef FEEDBACK_WIND
FLOAT FeedbackWindVel[3]; /*!< kick due to feedback force */
#endif
FLOAT HydroAccel[3]; /*!< acceleration due to hydrodynamical force */
FLOAT VelPred[3]; /*!< predicted SPH particle velocity at the current time */
FLOAT DivVel; /*!< local velocity divergence */
FLOAT CurlVel; /*!< local velocity curl */
FLOAT Rot[3]; /*!< local velocity curl */
FLOAT DhsmlDensityFactor; /*!< correction factor needed in the equation of motion of the conservative entropy formulation of SPH */
FLOAT MaxSignalVel; /*!< maximum "signal velocity" occuring for this particle */
#ifdef MULTIPHASE
int Phase;
int StickyIndex;
int StickyNgb;
int StickyMaxID;
float StickyMaxFs;
FLOAT StickyNewVel[3];
#endif
#ifdef OUTPUTOPTVAR1
FLOAT OptVar1; /*!< optional variable 1 */
#endif
#ifdef OUTPUTOPTVAR2
FLOAT OptVar2; /*!< optional variable 2 */
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
FLOAT VelocityDispersion[VELOCITY_DISPERSION_SIZE]; /*!< velocity dispersion */
#endif
#ifdef CHIMIE
FLOAT Metal[NELEMENTS];
FLOAT dMass; /*!< mass variation due to mass transfere */
#ifdef CHIMIE_THERMAL_FEEDBACK
FLOAT DeltaEgySpec;
FLOAT SNIaThermalTime; /*!< flag particles that got energy from SNIa */
FLOAT SNIIThermalTime; /*!< flag particles that got energy from SNII */
double NumberOfSNIa;
double NumberOfSNII;
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
FLOAT WindTime; /*!< flag particles that belongs to the wind */
unsigned int WindFlag; /*!< flag particles that will be part of the wind */
#endif
#endif /*CHIMIE*/
#ifdef ENTROPYPRED
FLOAT EntropyPred; /*!< predicted entropy at the current time */
#endif
#ifdef ART_CONDUCTIVITY
FLOAT EnergyIntPred;
FLOAT GradEnergyInt[3];
#endif
}
*SphP, /*!< holds SPH particle data on local processor */
*DomainSphBuf; /*!< buffer for SPH particle data in domain decomposition */
#ifdef STELLAR_PROP
/* the following struture holds data that is stored for each SPH particle in addition to the collisionless
* variables.
*/
extern struct st_particle_data
{
#ifdef CHECK_ID_CORRESPONDENCE
unsigned int ID; /*!< particle identifier (must be the same as P[].ID) only used to check ID correspondance */
#endif
FLOAT FormationTime; /*!< star formation time of particle */
FLOAT InitialMass; /*!< initial stellar mass */
#ifndef LONGIDS
unsigned int IDProj; /*!< id of projenitor particle */
#else
unsigned long long IDProj; /*!< id of projenitor particle */
#endif
FLOAT Metal[NELEMENTS];
FLOAT Density; /*!< current baryonic mass density of particle */
FLOAT Volume; /*!< current volume of particle */
FLOAT Hsml; /*!< current smoothing length */
FLOAT Left; /*!< lower bound in iterative smoothing length search */
FLOAT Right; /*!< upper bound in iterative smoothing length search */
FLOAT NumNgb; /*!< weighted number of neighbours found */
unsigned int PIdx; /*!< index to the corresponding particle */
#ifdef AVOIDNUMNGBPROBLEM
FLOAT OldNumNgb;
#endif
FLOAT DhsmlDensityFactor; /*!< correction factor needed in the equation of motion of the conservative entropy formulation of SPH */
double TotalEjectedGasMass;
double TotalEjectedEltMass[NELEMENTS];
double TotalEjectedEgySpec;
double NumberOfSNIa;
double NumberOfSNII;
#ifdef CHIMIE_KINETIC_FEEDBACK
double NgbMass; /*!< mass of neighbours */
#endif
#ifdef CHIMIE
unsigned int Flag;
#endif
}
*StP, /*!< holds ST particle data on local processor */
*DomainStBuf; /*!< buffer for ST particle data in domain decomposition */
#endif
/* Variables for Tree
*/
extern int MaxNodes; /*!< maximum allowed number of internal nodes */
extern int Numnodestree; /*!< number of (internal) nodes in each tree */
extern struct NODE
{
FLOAT len; /*!< sidelength of treenode */
FLOAT center[3]; /*!< geometrical center of node */
#ifdef ADAPTIVE_GRAVSOFT_FORGAS
FLOAT maxsoft; /*!< hold the maximum gravitational softening of particles in the
node if the ADAPTIVE_GRAVSOFT_FORGAS option is selected */
#endif
#ifdef STELLAR_FLUX
FLOAT starlum ; /*!< star luminosity of node */
#endif
union
{
int suns[8]; /*!< temporary pointers to daughter nodes */
struct
{
FLOAT s[3]; /*!< center of mass of node */
FLOAT mass; /*!< mass of node */
int bitflags; /*!< a bit-field with various information on the node */
int sibling; /*!< this gives the next node in the walk in case the current node can be used */
int nextnode; /*!< this gives the next node in case the current node needs to be opened */
int father; /*!< this gives the parent node of each node (or -1 if we have the root node) */
}
d;
}
u;
}
*Nodes_base, /*!< points to the actual memory allocted for the nodes */
*Nodes; /*!< this is a pointer used to access the nodes which is shifted such that Nodes[All.MaxPart]
gives the first allocated node */
extern int *Nextnode; /*!< gives next node in tree walk */
extern int *Father; /*!< gives parent node in tree */
extern struct extNODE /*!< this structure holds additional tree-node information which is not needed in the actual gravity computation */
{
FLOAT hmax; /*!< maximum SPH smoothing length in node. Only used for gas particles */
FLOAT vs[3]; /*!< center-of-mass velocity */
}
*Extnodes_base, /*!< points to the actual memory allocted for the extended node information */
*Extnodes; /*!< provides shifted access to extended node information, parallel to Nodes/Nodes_base */
/*! Header for the standard file format.
*/
extern struct io_header
{
int npart[6]; /*!< number of particles of each type in this file */
double mass[6]; /*!< mass of particles of each type. If 0, then the masses are explicitly
stored in the mass-block of the snapshot file, otherwise they are omitted */
double time; /*!< time of snapshot file */
double redshift; /*!< redshift of snapshot file */
int flag_sfr; /*!< flags whether the simulation was including star formation */
int flag_feedback; /*!< flags whether feedback was included (obsolete) */
unsigned int npartTotal[6]; /*!< total number of particles of each type in this snapshot. This can be
different from npart if one is dealing with a multi-file snapshot. */
int flag_cooling; /*!< flags whether cooling was included */
int num_files; /*!< number of files in multi-file snapshot */
double BoxSize; /*!< box-size of simulation in case periodic boundaries were used */
double Omega0; /*!< matter density in units of critical density */
double OmegaLambda; /*!< cosmological constant parameter */
double HubbleParam; /*!< Hubble parameter in units of 100 km/sec/Mpc */
int flag_stellarage; /*!< flags whether the file contains formation times of star particles */
int flag_metals; /*!< flags whether the file contains metallicity values for gas and star particles */
unsigned int npartTotalHighWord[6]; /*!< High word of the total number of particles of each type */
int flag_entropy_instead_u; /*!< flags that IC-file contains entropy instead of u */
int flag_chimie_extraheader; /*!< flags that IC-file contains extra-header for chimie */
#ifdef MULTIPHASE
double critical_energy_spec;
#ifdef MESOMACHINE
char fill[38];
#else
char fill[48]; /* use 42 with regor... */
#endif
#else
char fill[56]; /*!< fills to 256 Bytes */
#endif
}
header; /*!< holds header for snapshot files */
#ifdef CHIMIE_EXTRAHEADER
/*! Header for the chimie part.
*/
extern struct io_chimie_extraheader
{
int nelts; /*!< number of chemical element followed */
float SolarAbundances[NELEMENTS];
char labels[256-4-4*(NELEMENTS)];
}
chimie_extraheader;
#endif
#define IO_NBLOCKS 24 /*!< total number of defined information blocks for snapshot files.
Must be equal to the number of entries in "enum iofields" */
enum iofields /*!< this enumeration lists the defined output blocks in snapshot files. Not all of them need to be present. */
{
IO_POS,
IO_VEL,
IO_ID,
IO_MASS,
IO_U,
IO_RHO,
IO_HSML,
IO_POT,
IO_ACCEL,
IO_DTENTR,
IO_TSTP,
IO_ERADSPH,
IO_ERADSTICKY,
IO_ERADFEEDBACK,
IO_ENERGYFLUX,
IO_METALS,
IO_STAR_FORMATIONTIME,
IO_INITIAL_MASS,
IO_STAR_IDPROJ,
IO_STAR_RHO,
IO_STAR_HSML,
IO_STAR_METALS,
IO_OPTVAR1,
IO_OPTVAR2
};
extern char Tab_IO_Labels[IO_NBLOCKS][4]; /*<! This table holds four-byte character tags used for fileformat 2 */
/* global state of system, used for global statistics
*/
extern struct state_of_system
{
double Mass;
double EnergyKin;
double EnergyPot;
double EnergyInt;
#ifdef COOLING
double EnergyRadSph;
#endif
#ifdef AGN_HEATING
double EnergyAGNHeat;
#endif
#ifdef MULTIPHASE
double EnergyRadSticky;
#endif
#ifdef FEEDBACK_WIND
double EnergyFeedbackWind;
#endif
#ifdef BUBBLES
double EnergyBubbles;
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
double EnergyThermalFeedback;
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
double EnergyKineticFeedback;
#endif
double EnergyTot;
double Momentum[4];
double AngMomentum[4];
double CenterOfMass[4];
double MassComp[6];
double EnergyKinComp[6];
double EnergyPotComp[6];
double EnergyIntComp[6];
#ifdef COOLING
double EnergyRadSphComp[6];
#endif
#ifdef AGN_HEATING
double EnergyAGNHeatComp[6];
#endif
#ifdef MULTIPHASE
double EnergyRadStickyComp[6];
#endif
#ifdef FEEDBACK_WIND
double EnergyFeedbackWindComp[6];
#endif
#ifdef BUBBLES
double EnergyBubblesComp[6];
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
double EnergyThermalFeedbackComp[6];
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
double EnergyKineticFeedbackComp[6];
#endif
double EnergyTotComp[6];
double MomentumComp[6][4];
double AngMomentumComp[6][4];
double CenterOfMassComp[6][4];
}
SysState; /*<! Structure for storing some global statistics about the simulation. */
/*! This structure contains data related to the energy budget.
These values are different for each task. It need to be stored
in the restart flag.
*/
extern struct local_state_of_system
{
double EnergyTest;
double EnergyInt1;
double EnergyInt2;
double EnergyKin1;
double EnergyKin2;
#ifdef COOLING
double RadiatedEnergy;
#endif
#ifdef SFR
double StarEnergyInt;
#ifdef FEEDBACK
double StarEnergyFeedback;
#endif
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
double EnergyThermalFeedback;
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
double EnergyKineticFeedback;
#endif
#ifdef MULTIPHASE
double EnergyRadSticky;
#endif
#ifdef FEEDBACK_WIND
double EnergyFeedbackWind;
#endif
}
LocalSysState; /*<! Structure for storing some local statistics about the simulation. */
/* Various structures for communication
*/
extern struct gravdata_in
{
union
{
FLOAT Pos[3];
FLOAT Acc[3];
FLOAT Potential;
}
u;
#if defined(UNEQUALSOFTENINGS) || defined(STELLAR_FLUX)
int Type;
#ifdef ADAPTIVE_GRAVSOFT_FORGAS
FLOAT Soft;
#endif
#endif
#ifdef STELLAR_FLUX
FLOAT EnergyFlux;
#endif
union
{
FLOAT OldAcc;
int Ninteractions;
}
w;
}
*GravDataIn, /*!< holds particle data to be exported to other processors */
*GravDataGet, /*!< holds particle data imported from other processors */
*GravDataResult, /*!< holds the partial results computed for imported particles. Note: We use GravDataResult = GravDataGet, such that the result replaces the imported data */
*GravDataOut; /*!< holds partial results received from other processors. This will overwrite the GravDataIn array */
extern struct gravdata_index
{
int Task;
int Index;
int SortIndex;
}
*GravDataIndexTable; /*!< the particles to be exported are grouped by task-number. This table allows the results to be disentangled again and to be assigned to the correct particle */
extern struct densdata_in
{
FLOAT Pos[3];
FLOAT Vel[3];
FLOAT Hsml;
#ifdef MULTIPHASE
int Phase;
#endif
int Index;
int Task;
#ifdef ART_CONDUCTIVITY
FLOAT EnergyIntPred;
#endif
}
*DensDataIn, /*!< holds particle data for SPH density computation to be exported to other processors */
*DensDataGet; /*!< holds imported particle data for SPH density computation */
extern struct densdata_out
{
FLOAT Rho;
FLOAT Div, Rot[3];
FLOAT DhsmlDensity;
FLOAT Ngb;
#ifdef ART_CONDUCTIVITY
FLOAT GradEnergyInt[3];
#endif
}
*DensDataResult, /*!< stores the locally computed SPH density results for imported particles */
*DensDataPartialResult; /*!< imported partial SPH density results from other processors */
extern struct hydrodata_in
{
FLOAT Pos[3];
FLOAT Vel[3];
FLOAT Hsml;
#ifdef FEEDBACK
FLOAT EnergySN;
#endif
#ifdef MULTIPHASE
int Phase;
FLOAT Entropy;
int StickyFlag;
#endif
FLOAT Mass;
FLOAT Density;
FLOAT Pressure;
FLOAT F1;
FLOAT DhsmlDensityFactor;
int Timestep;
int Task;
int Index;
#ifdef WITH_ID_IN_HYDRA
int ID;
#endif
#ifdef ART_CONDUCTIVITY
FLOAT NormGradEnergyInt;
#endif
}
*HydroDataIn, /*!< holds particle data for SPH hydro-force computation to be exported to other processors */
*HydroDataGet; /*!< holds imported particle data for SPH hydro-force computation */
extern struct hydrodata_out
{
FLOAT Acc[3];
FLOAT DtEntropy;
#ifdef FEEDBACK
FLOAT DtEgySpecFeedback;
FLOAT FeedbackAccel[3]; /*!< acceleration due to feedback force */
#endif
FLOAT MaxSignalVel;
#ifdef COMPUTE_VELOCITY_DISPERSION
FLOAT VelocityDispersion[VELOCITY_DISPERSION_SIZE];
#endif
#ifdef MULTIPHASE
FLOAT StickyDVel[3]; /*!< differences in velocities induced by sticky collisions */
#endif
#ifdef OUTPUT_CONDUCTIVITY
FLOAT OptVar2;
#endif
}
*HydroDataResult, /*!< stores the locally computed SPH hydro results for imported particles */
*HydroDataPartialResult; /*!< imported partial SPH hydro-force results from other processors */
#ifdef MULTIPHASE
extern struct stickydata_in
{
FLOAT Pos[3];
FLOAT Vel[3];
FLOAT Mass;
FLOAT Hsml;
int ID;
int StickyMaxID;
int StickyNgb;
float StickyMaxFs;
int Task;
int Index;
}
*StickyDataIn, /*!< holds particle data for sticky computation to be exported to other processors */
*StickyDataGet; /*!< holds imported particle data for sticky computation */
extern struct stickydata_out
{
int StickyMaxID;
int StickyNgb;
float StickyMaxFs;
FLOAT StickyNewVel[3];
}
*StickyDataResult, /*!< stores the locally computed sticky results for imported particles */
*StickyDataPartialResult; /*!< imported partial sticky results from other processors */
extern struct Sticky_index
{
int Index;
int CellIndex;
int Flag;
}
*StickyIndex;
#endif
#ifdef CHIMIE
extern struct chimiedata_in
{
FLOAT Pos[3];
FLOAT Vel[3];
#ifndef LONGIDS
unsigned int ID; /*!< particle identifier */
#else
unsigned long long ID; /*!< particle identifier */
#endif
FLOAT Hsml;
#ifdef FEEDBACK
FLOAT EnergySN;
#endif
#ifdef MULTIPHASE
int Phase;
FLOAT Entropy;
int StickyFlag;
#endif
FLOAT Density;
FLOAT Volume;
FLOAT Pressure;
FLOAT F1;
FLOAT DhsmlDensityFactor;
int Timestep;
int Task;
int Index;
#ifdef WITH_ID_IN_HYDRA
int ID;
#endif
double TotalEjectedGasMass;
double TotalEjectedEltMass[NELEMENTS];
double TotalEjectedEgySpec;
double NumberOfSNIa;
double NumberOfSNII;
#ifdef CHIMIE_KINETIC_FEEDBACK
FLOAT NgbMass;
#endif
}
*ChimieDataIn, /*!< holds particle data for Chimie computation to be exported to other processors */
*ChimieDataGet; /*!< holds imported particle data for Chimie computation */
extern struct chimiedata_out
{
FLOAT Acc[3];
FLOAT DtEntropy;
#ifdef FEEDBACK
FLOAT DtEgySpecFeedback;
FLOAT FeedbackAccel[3]; /*!< acceleration due to feedback force */
#endif
FLOAT MaxSignalVel;
#ifdef COMPUTE_VELOCITY_DISPERSION
FLOAT VelocityDispersion[VELOCITY_DISPERSION_SIZE];
#endif
#ifdef MULTIPHASE
FLOAT StickyDVel[3]; /*!< differences in velocities induced by sticky collisions */
#endif
}
*ChimieDataResult, /*!< stores the locally computed Chimie results for imported particles */
*ChimieDataPartialResult; /*!< imported partial Chimie results from other processors */
extern struct starsdensdata_in
{
FLOAT Pos[3];
FLOAT Hsml;
int Index;
int Task;
}
*StarsDensDataIn, /*!< holds particle data for SPH density computation to be exported to other processors */
*StarsDensDataGet; /*!< holds imported particle data for SPH density computation */
extern struct starsdensdata_out
{
FLOAT Rho;
FLOAT Volume;
FLOAT DhsmlDensity;
FLOAT Ngb;
#ifdef CHIMIE_KINETIC_FEEDBACK
FLOAT NgbMass;
#endif
}
*StarsDensDataResult, /*!< stores the locally computed SPH density results for imported particles */
*StarsDensDataPartialResult; /*!< imported partial SPH density results from other processors */
#endif
#endif
diff --git a/chimie.c b/chimie.c
index 8b5aa59..143adcd 100644
--- a/chimie.c
+++ b/chimie.c
@@ -1,2774 +1,2833 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include <gsl/gsl_math.h>
#include "allvars.h"
#include "proto.h"
#ifdef CHIMIE
/*! \file hydra.c
* \brief Computation of SPH forces and rate of entropy generation
*
* This file contains the "second SPH loop", where the SPH forces are
* computed, and where the rate of change of entropy due to the shock heating
* (via artificial viscosity) is computed.
*/
static double hubble_a, atime, hubble_a2, fac_mu, fac_vsic_fix, a3inv, fac_egy;
#ifdef FEEDBACK
static double fac_pow;
#endif
#ifdef PERIODIC
static double boxSize, boxHalf;
#ifdef LONG_X
static double boxSize_X, boxHalf_X;
#else
#define boxSize_X boxSize
#define boxHalf_X boxHalf
#endif
#ifdef LONG_Y
static double boxSize_Y, boxHalf_Y;
#else
#define boxSize_Y boxSize
#define boxHalf_Y boxHalf
#endif
#ifdef LONG_Z
static double boxSize_Z, boxHalf_Z;
#else
#define boxSize_Z boxSize
#define boxHalf_Z boxHalf
#endif
#endif
/****************************************************************************************/
/*
/*
/*
/* GADGET CHIMIE PART
/*
/*
/*
/****************************************************************************************/
#define MAXPTS 10
#define MAXDATASIZE 200
#define KPC_IN_CM 3.085e+21
static int verbose=0;
static double *MassFracSNII;
static double *SingleMassFracSNII;
static double *EjectedMass;
static double *SingleEjectedMass;
static double **MassFracSNIIs;
static double **SingleMassFracSNIIs;
static double **EjectedMasss;
static double **SingleEjectedMasss;
/* intern global variables */
static struct local_params_chimie
{
float coeff_z[3][3];
float Mmin,Mmax;
int n;
float ms[MAXPTS];
float as[MAXPTS+1];
float bs[MAXPTS+1];
float fs[MAXPTS];
double imf_Ntot;
float SNII_Mmin;
float SNII_Mmax;
float SNII_cte;
float SNII_a;
float SNIa_Mpl;
float SNIa_Mpu;
float SNIa_a;
float SNIa_cte;
float SNIa_Mdl1;
float SNIa_Mdu1;
float SNIa_a1;
float SNIa_b1;
float SNIa_cte1;
float SNIa_bb1;
float SNIa_Mdl2;
float SNIa_Mdu2;
float SNIa_a2;
float SNIa_b2;
float SNIa_cte2;
float SNIa_bb2;
float Mco;
int npts;
int nelts;
}
*Cps,*Cp;
static struct local_elts_chimie
{
float Mmin; /* minimal mass */
float Step; /* log of mass step */
float Array[MAXDATASIZE]; /* data */
float Metal[MAXDATASIZE]; /* data */
float MSNIa;
float SolarAbundance;
char label[72];
}
**Elts,*Elt;
void allocate_chimie()
{
int j;
/* allocate Cp */
Cps = malloc((All.ChimieNumberOfParameterFiles) * sizeof(struct local_params_chimie));
/* allocate elts */
Elts = malloc((All.ChimieNumberOfParameterFiles) * sizeof(struct local_elts_chimie));
//for (j=0;j<All.ChimieNumberOfParameterFiles;j++)
// Elt[j] = malloc((nelts) * sizeof(struct local_elts_chimie));
MassFracSNIIs = malloc((All.ChimieNumberOfParameterFiles) * sizeof(double));
EjectedMasss = malloc((All.ChimieNumberOfParameterFiles) * sizeof(double));
SingleMassFracSNIIs= malloc((All.ChimieNumberOfParameterFiles) * sizeof(double));
SingleEjectedMasss = malloc((All.ChimieNumberOfParameterFiles) * sizeof(double));
}
void allocate_Elts(int it)
{
/* allocate memory for elts */
if (Cps[it].npts<=MAXDATASIZE)
{
Elts[it] = malloc((Cps[it].nelts+2) * sizeof(struct local_elts_chimie));
}
else
{
printf("\n Cps[it].npts = %d > MAXDATASIZE = %d !!!\n\n",Cps[it].npts,MAXDATASIZE);
endrun(88800);
}
}
void set_table(int i)
{
if (i>=All.ChimieNumberOfParameterFiles)
{
printf("\n set_table : i>= %d !!!\n\n",All.ChimieNumberOfParameterFiles);
endrun(88809);
}
else
{
Cp = &Cps[i];
Elt = Elts[i];
MassFracSNII = MassFracSNIIs[i];
SingleMassFracSNII = SingleMassFracSNIIs[i];
EjectedMass = EjectedMasss[i];
SingleEjectedMass = SingleEjectedMasss[i];
}
}
void read_chimie(char * filename,int it)
{
char line[72],buffer[72];
FILE *fd;
int i,j;
if (ThisTask==0)
{
printf("reading %s ...\n",filename);
fd = fopen(filename,"r");
/* read Lifetime */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%g %g %g\n", &Cps[it].coeff_z[0][0],&Cps[it].coeff_z[0][1],&Cps[it].coeff_z[0][2]);
fscanf(fd, "%g %g %g\n", &Cps[it].coeff_z[1][0],&Cps[it].coeff_z[1][1],&Cps[it].coeff_z[1][2]);
fscanf(fd, "%g %g %g\n", &Cps[it].coeff_z[2][0],&Cps[it].coeff_z[2][1],&Cps[it].coeff_z[2][2]);
fgets(line, sizeof(line), fd);
/* IMF Parameters */
fgets(line, sizeof(line), fd);
fscanf(fd, "%g %g\n",&Cps[it].Mmin,&Cps[it].Mmax);
fscanf(fd, "%d\n",&Cps[it].n);
if (Cps[it].n>0)
for (i=0;i<Cps[it].n;i++)
fscanf(fd,"%g",&Cps[it].ms[i]);
else
fgets(line, sizeof(line), fd);
for (i=0;i<Cps[it].n+1;i++)
fscanf(fd,"%g",&Cps[it].as[i]);
fgets(line, sizeof(line), fd);
/* Parameters for SNII Rates */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%g \n",&Cps[it].SNII_Mmin);
fgets(line, sizeof(line), fd);
/* Parameters for SNIa Rates */
fgets(line, sizeof(line), fd);
fscanf(fd, "%g %g\n",&Cps[it].SNIa_Mpl,&Cps[it].SNIa_Mpu);
fscanf(fd, "%g \n",&Cps[it].SNIa_a);
fscanf(fd, "%g %g %g\n",&Cps[it].SNIa_Mdl1,&Cps[it].SNIa_Mdu1,&Cps[it].SNIa_bb1);
fscanf(fd, "%g %g %g\n",&Cps[it].SNIa_Mdl2,&Cps[it].SNIa_Mdu2,&Cps[it].SNIa_bb2);
fgets(line, sizeof(line), fd);
/* Metal injection SNII */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%d %d\n",&Cps[it].npts,&Cps[it].nelts);
/* allocate memory for elts */
allocate_Elts(it);
/* injected metals */
for (i=0;i<Cps[it].nelts+2;i++)
{
fgets(line, sizeof(line), fd);
/* strip trailing line */
for (j = 0; j < strlen(line); j++)
if ( line[j] == '\n' || line[j] == '\r' )
line[j] = '\0';
/* copy labels */
strcpy(Elts[it][i].label,line);
strcpy(buffer,&Elts[it][i].label[2]);
strcpy(Elts[it][i].label,buffer);
fgets(line, sizeof(line), fd);
fscanf(fd, "%g %g\n",&Elts[it][i].Mmin,&Elts[it][i].Step);
for (j=0;j<Cps[it].npts;j++)
{
fscanf(fd, "%g\n",&Elts[it][i].Metal[j]);
}
}
/* integrals of injected metals */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%d %d\n",&Cps[it].npts,&Cps[it].nelts);
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
/* integrals of injected metals */
for (i=0;i<Cps[it].nelts+2;i++)
{
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%g %g\n",&Elts[it][i].Mmin,&Elts[it][i].Step);
for (j=0;j<Cps[it].npts;j++)
{
fscanf(fd, "%g\n",&Elts[it][i].Array[j]);
}
}
/* Metal injection SNIa */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%g\n",&Cps[it].Mco);
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
int nelts;
char label[72];
fscanf(fd, "%d\n",&nelts);
/* check */
if (nelts != Cps[it].nelts)
{
printf("\nThe number of elements in SNII (=%d) is not identical to the on of SNIa (=%d) !!!\n\n",Cps[it].nelts,nelts);
printf("This is not supported by the current implementation !!!\n");
endrun(88805);
}
for (i=0;i<Cps[it].nelts+2;i++)
{
fgets(line, sizeof(line), fd); /* label */
/* check label */
/* strip trailing line */
for (j = 0; j < strlen(line); j++)
if ( line[j] == '\n' || line[j] == '\r' )
line[j] = '\0';
strcpy(label,line);
strcpy(buffer,&label[2]);
strcpy(label,buffer);
if (strcmp(label,Elts[it][i].label)!=0)
{
printf("\nLabel of SNII element %d (=%s) is different from the SNIa one (=%s) !!!\n\n",i,Elts[it][i].label,label);
endrun(88806);
}
//fgets(line, sizeof(line), fd);
fscanf(fd, "%g\n",&Elts[it][i].MSNIa);
}
/* Solar Abundances */
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fgets(line, sizeof(line), fd);
fscanf(fd, "%d\n",&nelts);
/* check */
if (nelts != Cps[it].nelts)
{
printf("\nThe number of elements in SolarAbundances (=%d) is not identical to the on of SNIa (=%d) !!!\n\n",Cps[it].nelts,nelts);
printf("This is not supported by the current implementation !!!\n");
endrun(88805);
}
for (i=0;i<Cps[it].nelts;i++)
{
fgets(line, sizeof(line), fd); /* label */
/* check label */
/* strip trailing line */
for (j = 0; j < strlen(line); j++)
if ( line[j] == '\n' || line[j] == '\r' )
line[j] = '\0';
strcpy(label,line);
strcpy(buffer,&label[2]);
strcpy(label,buffer);
if (strcmp(label,Elts[it][i+2].label)!=0)
{
printf("\nLabel of SNII element %d (=%s) is different from the SNIa one (=%s) !!!\n\n",i,Elts[it][i+2].label,label);
endrun(88806);
}
//fgets(line, sizeof(line), fd);
fscanf(fd, "%g\n",&Elts[it][i+2].SolarAbundance);
}
fclose(fd);
}
/* send Cps */
MPI_Bcast(Cps, (All.ChimieNumberOfParameterFiles) * sizeof(struct local_params_chimie), MPI_BYTE, 0, MPI_COMM_WORLD);
/* slaves allocate Elts */
if (ThisTask!=0)
allocate_Elts(it);
MPI_Bcast(Elts[it], (Cps[it].nelts+2) * sizeof(struct local_elts_chimie), MPI_BYTE, 0, MPI_COMM_WORLD);
/* allocate memory */
MassFracSNIIs[it] = malloc((Cps[it].nelts+2) * sizeof(double));
EjectedMasss[it] = malloc((Cps[it].nelts+2) * sizeof(double));
SingleMassFracSNIIs[it] = malloc((Cps[it].nelts+2) * sizeof(double));
SingleEjectedMasss[it] = malloc((Cps[it].nelts+2) * sizeof(double));
if (verbose && ThisTask==0)
{
printf("%g %g %g\n", Cps[it].coeff_z[0][0],Cps[it].coeff_z[0][1],Cps[it].coeff_z[0][2]);
printf("%g %g %g\n", Cps[it].coeff_z[1][0],Cps[it].coeff_z[1][1],Cps[it].coeff_z[1][2]);
printf("%g %g %g\n", Cps[it].coeff_z[2][0],Cps[it].coeff_z[2][1],Cps[it].coeff_z[2][2]);
printf("\n");
printf("\nIMF\n");
printf("%g %g\n",Cps[it].Mmin,Cps[it].Mmax);
printf("%d\n",Cps[it].n);
for (i=0;i<Cps[it].n;i++)
printf( "ms : %g ",Cps[it].ms[i]);
printf("\n");
for (i=0;i<Cps[it].n+1;i++)
printf( "as : %g ",Cps[it].as[i]);
printf("\n");
printf("\nRate SNII\n");
printf("%g ",Cps[it].SNII_Mmin);
printf("\n");
printf("\nRate SNIa\n");
printf("%g %g\n",Cps[it].SNIa_Mpl,Cps[it].SNIa_Mpu);
printf("%g \n",Cps[it].SNIa_a);
printf("%g %g %g\n",Cps[it].SNIa_Mdl1,Cps[it].SNIa_Mdu1,Cps[it].SNIa_b1);
printf("%g %g %g\n",Cps[it].SNIa_Mdl2,Cps[it].SNIa_Mdu2,Cps[it].SNIa_b2);
printf("\n");
for (i=0;i<Cps[it].nelts+2;i++)
{
printf("> %g %g\n",Elts[it][i].Mmin,Elts[it][i].Step);
for (j=0;j<Cps[it].npts;j++)
{
printf(" %g\n",Elts[it][i].Array[j]);
}
}
printf("\n");
printf("%g\n",Cps[it].Mco);
for (i=0;i<Cps[it].nelts+2;i++)
printf("%g\n",Elts[it][i].MSNIa);
printf("\n");
}
}
/*
This function returns the mass fraction of a star of mass m
using the current IMF
*/
static double get_imf(double m)
{
int i;
int n;
n = Cp->n;
/* convert m in msol */
m = m*All.UnitMass_in_g / SOLAR_MASS;
if (n==0)
return Cp->bs[0]* pow(m,Cp->as[0]);
else
{
for (i=0;i<n;i++)
if (m < Cp->ms[i])
return Cp->bs[i]* pow(m,Cp->as[i]);
return Cp->bs[n]* pow(m,Cp->as[n]);
}
}
/*
This function returns the mass fraction between m1 and m2
per mass unit, using the current IMF
*/
static double get_imf_M(double m1, double m2)
{
int i;
int n;
double p;
double integral=0;
double mmin,mmax;
n = Cp->n;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
m2 = m2*All.UnitMass_in_g / SOLAR_MASS;
if (n==0)
{
p = Cp->as[0]+1;
integral = (Cp->bs[0]/p) * ( pow(m2,p) - pow(m1,p) );
//printf("--> %g %g %g %g int=%g\n",m1,m2,pow(m2,p), pow(m1,p),integral);
}
else
{
integral = 0;
/* first */
if (m1<Cp->ms[0])
{
mmin = m1;
mmax = dmin(Cp->ms[0],m2);
p = Cp->as[0] + 1;
integral += (Cp->bs[0]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
/* last */
if (m2>Cp->ms[n-1])
{
mmin = dmax(Cp->ms[n-1],m1);
mmax = m2;
p = Cp->as[n] + 1;
integral += (Cp->bs[n]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
/* loop over other segments */
for (i=0;i<n-1;i++)
{
mmin = dmax(Cp->ms[i ],m1);
mmax = dmin(Cp->ms[i+1],m2);
if (mmin<mmax)
{
p = Cp->as[i+1] + 1;
integral += (Cp->bs[i+1]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
}
}
/* convert into mass unit mass unit */
/* integral = integral * SOLAR_MASS/All.UnitMass_in_g;*/
return integral;
}
/*
This function returns the number fraction between m1 and m2
per mass unit, using the current IMF
*/
static double get_imf_N(double m1, double m2)
{
int i;
int n;
double p;
double integral=0;
double mmin,mmax;
n = Cp->n;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
m2 = m2*All.UnitMass_in_g / SOLAR_MASS;
if (n==0)
{
p = Cp->as[0];
integral = (Cp->bs[0]/p) * ( pow(m2,p) - pow(m1,p) );
}
else
{
integral = 0;
/* first */
if (m1<Cp->ms[0])
{
mmin = m1;
mmax = dmin(Cp->ms[0],m2);
p = Cp->as[0];
integral += (Cp->bs[0]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
/* last */
if (m2>Cp->ms[n-1])
{
mmin = dmax(Cp->ms[n-1],m1);
mmax = m2;
p = Cp->as[n];
integral += (Cp->bs[n]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
/* loop over other segments */
for (i=0;i<n-1;i++)
{
mmin = dmax(Cp->ms[i ],m1);
mmax = dmin(Cp->ms[i+1],m2);
if (mmin<mmax)
{
p = Cp->as[i+1];
integral += (Cp->bs[i+1]/p) * ( pow(mmax,p) - pow(mmin,p) );
}
}
}
/* convert into mass unit mass unit */
integral = integral / SOLAR_MASS*All.UnitMass_in_g;
return integral;
}
/*
This function returns the number fraction between m1 and m2
per mass unit, using the current IMF
*/
static double imf_sampling()
{
int i;
int n;
double m;
double f;
double pmin,pmax;
n = Cp->n;
/* init random */
//srandom(irand);
f = (double)random()/(double)RAND_MAX;
if (n==0)
{
pmin = pow(Cp->Mmin,Cp->as[0]);
pmax = pow(Cp->Mmax,Cp->as[0]);
m = pow(f*(pmax - pmin) + pmin ,1./Cp->as[0]);
return m* SOLAR_MASS/All.UnitMass_in_g;
}
else
{
if (f<Cp->fs[0])
{
pmin = pow(Cp->Mmin ,Cp->as[0]);
m = pow(Cp->imf_Ntot*Cp->as[0]/Cp->bs[0]* (f-0) + pmin ,1./Cp->as[0]);
return m* SOLAR_MASS/All.UnitMass_in_g;
}
for (i=0;i<n-1;i++)
{
if (f<Cp->fs[i+1])
{
pmin = pow(Cp->ms[i] ,Cp->as[i+1]);
m = pow(Cp->imf_Ntot*Cp->as[i+1]/Cp->bs[i+1]* (f-Cp->fs[i]) + pmin ,1./Cp->as[i+1]);
return m* SOLAR_MASS/All.UnitMass_in_g;
}
}
/* last portion */
pmin = pow(Cp->ms[n-1] ,Cp->as[n]);
m = pow(Cp->imf_Ntot*Cp->as[n]/Cp->bs[n]* (f-Cp->fs[n-1]) + pmin ,1./Cp->as[n]);
return m* SOLAR_MASS/All.UnitMass_in_g;
}
}
/*
This function initialized the imf parameters
defined in the chimie file
*/
void init_imf(void)
{
float integral = 0;
float p;
float cte;
int i,n;
double mmin,mmax;
n = Cp->n;
if (n==0)
{
p = Cp->as[0]+1;
integral = integral + ( pow(Cp->Mmax,p)-pow(Cp->Mmin,p))/(p) ;
Cp->bs[0] = 1./integral ;
}
else
{
cte = 1.0;
if (Cp->Mmin < Cp->ms[0])
{
p = Cp->as[0]+1;
integral = integral + (pow(Cp->ms[0],p) - pow(Cp->Mmin,p))/p;
}
for (i=0;i<n-1;i++)
{
cte = cte* pow( Cp->ms[i],( Cp->as[i] - Cp->as[i+1] ));
p = Cp->as[i+1]+1;
integral = integral + cte*(pow(Cp->ms[i+1],p) - pow(Cp->ms[i],p))/p;
}
if (Cp->Mmax > Cp->ms[-1])
{
cte = cte* pow( Cp->ms[n-1] , ( Cp->as[n-1] - Cp->as[n] ) );
p = Cp->as[n]+1;
integral = integral + cte*(pow(Cp->Mmax,p) - pow(Cp->ms[n-1],p))/p;
}
/* compute all b */
Cp->bs[0] = 1./integral;
for (i=0;i<n;i++)
{
Cp->bs[i+1] = Cp->bs[i] * pow( Cp->ms[i],( Cp->as[i] - Cp->as[i+1] ));
}
}
if (verbose && ThisTask==0)
{
printf("-- bs -- \n");
for (i=0;i<n+1;i++)
printf("%g ",Cp->bs[i]);
printf("\n");
}
mmin = Cp->Mmin / All.UnitMass_in_g * SOLAR_MASS; /* in mass unit */
mmax = Cp->Mmax / All.UnitMass_in_g * SOLAR_MASS; /* in mass unit */
Cp->imf_Ntot = get_imf_N(mmin,mmax) *SOLAR_MASS/All.UnitMass_in_g;
/* init fs : mass fraction at ms */
if (n>0)
{
for (i=0;i<n+1;i++)
{
mmax = Cp->ms[i] / All.UnitMass_in_g * SOLAR_MASS; /* in mass unit */
Cp->fs[i] = SOLAR_MASS/All.UnitMass_in_g*get_imf_N(mmin,mmax)/Cp->imf_Ntot;
}
}
}
/*
This function init the chime parameters
*/
void init_chimie(void)
{
int i,nf;
double u_lt;
double UnitLength_in_kpc;
double UnitMass_in_Msol;
char filename[500];
char ext[100];
/* check some flags */
#ifndef COSMICTIME
if (All.ComovingIntegrationOn)
{
if(ThisTask == 0)
printf("Code wasn't compiled with COSMICTIME support enabled!\n");
endrun(-88800);
}
#endif
UnitLength_in_kpc = All.UnitLength_in_cm / KPC_IN_CM;
UnitMass_in_Msol = All.UnitMass_in_g / SOLAR_MASS;
//u_lt = -log10( 4.7287e11*sqrt(pow(UnitLength_in_kpc,3)/UnitMass_in_Msol));
/*Sat Dec 25 23:27:10 CET 2010 */
u_lt = -log10(All.UnitTime_in_Megayears*1e6);
allocate_chimie();
for (nf=0;nf<All.ChimieNumberOfParameterFiles;nf++)
{
if (All.ChimieNumberOfParameterFiles==1)
sprintf(filename,"%s",All.ChimieParameterFile);
else
sprintf(filename,"%s.%d",All.ChimieParameterFile,nf);
read_chimie(filename,nf);
/* set the table */
set_table(nf);
/* Conversion into program time unit */
Cp->coeff_z[2][2] = Cp->coeff_z[2][2] + u_lt;
for (i=0;i<3;i++)
Cp->coeff_z[1][i] = Cp->coeff_z[1][i]/2.0;
/* init imf parameters */
init_imf();
/* init SNII parameters */
if (Cp->n==0)
{
//Cp->SNII_cte[0] = Cp->bs[0]/Cp->as[0];
Cp->SNII_cte = Cp->bs[0]/Cp->as[0];
Cp->SNII_a = Cp->as[0];
}
else
{
//for (i=0;i<Cp->n+1;i++) /* if multiple power law in the SNII mass range */
// Cp->SNII_cte[i] = Cp->bs[i]/Cp->as[i];
Cp->SNII_cte = Cp->bs[Cp->n]/Cp->as[Cp->n];
Cp->SNII_a = Cp->as[Cp->n];
}
/* init SNIa parameters */
Cp->SNIa_a1 = Cp->SNIa_a;
Cp->SNIa_b1 = (Cp->SNIa_a1+1)/(pow(Cp->SNIa_Mdu1,Cp->SNIa_a1+1)-pow(Cp->SNIa_Mdl1,Cp->SNIa_a1+1));
Cp->SNIa_cte1 = Cp->SNIa_b1/Cp->SNIa_a1;
Cp->SNIa_a2 = Cp->SNIa_a;
Cp->SNIa_b2 = (Cp->SNIa_a2+1)/(pow(Cp->SNIa_Mdu2,Cp->SNIa_a2+1)-pow(Cp->SNIa_Mdl2,Cp->SNIa_a2+1));
Cp->SNIa_cte2 = Cp->SNIa_b2/Cp->SNIa_a2;
/* init SNII parameters */
if (Cp->n==0)
{
Cp->SNIa_cte = Cp->bs[0]/Cp->as[0];
Cp->SNIa_a = Cp->as[0];
}
else
{
Cp->SNIa_cte = Cp->bs[Cp->n]/Cp->as[Cp->n];
Cp->SNIa_a = Cp->as[Cp->n];
}
Cp->SNII_Mmax = Cp->Mmax;
for (i=0;i<Cp->nelts+2;i++)
Elt[i].Mmin = log10(Elt[i].Mmin);
/* output info */
if (verbose && ThisTask==0)
{
printf("-- SNII_cte -- \n");
//for (i=0;i<Cp->n+1;i++)
// printf("%g ",Cp->SNII_cte[i]);
printf("%g ",Cp->SNII_cte);
printf("\n");
}
/* check that the masses are higher than the last IMF elbow */
if (Cp->n>0)
{
if (Cp->SNIa_Mpl < Cp->ms[Cp->n-1])
{
printf("\nSNIa_Mpl = %g < ms[n-1] = %g !!!\n\n",Cp->SNIa_Mpl,Cp->ms[Cp->n-1]);
printf("This is not supported by the current implementation !!!\n");
endrun(88801);
}
if (Cp->SNIa_Mpu < Cp->ms[Cp->n-1])
{
printf("\nSNIa_Mpu = %g < ms[n-1] = %g !!!\n\n",Cp->SNIa_Mpu,Cp->ms[Cp->n-1]);
printf("This is not supported by the current implementation !!!\n");
endrun(88802);
}
if (Cp->SNII_Mmin < Cp->ms[Cp->n-1])
{
printf("\nSNII_Mmin = %g < ms[n-1] = %g !!!\n\n",Cp->SNII_Mmin,Cp->ms[Cp->n-1]);
printf("This is not supported by the current implementation !!!\n");
endrun(88803);
}
if (Cp->SNII_Mmax < Cp->ms[Cp->n-1])
{
printf("\nSNII_Mmax = %g < ms[n-1] = %g !!!\n\n",Cp->SNII_Mmax,Cp->ms[Cp->n-1]);
printf("This is not supported by the current implementation !!!\n");
endrun(88804);
}
}
}
}
void check_chimie(void)
{
int i;
if (ThisTask==0)
{
printf("Number of elements : %d\n",Cp->nelts);
for(i=2;i<Cp->nelts+2;i++)
printf("%s ",&Elt[i].label);
printf("\n");
}
/* check number of elements */
if (NELEMENTS != Cp->nelts)
{
printf("\n(Taks=%d) NELEMENTS (=%d) != Cp->nelts (=%d)!!!\n\n",ThisTask,NELEMENTS,Cp->nelts);
printf("This means that the number of chemical elements defined in the chimie parameter file is\n");
printf("different from the one defined by the variable NELEMENTS.\n");
printf("You can either change NELEMENTS and recompile the code or change the chimie parameter file.\n");
endrun(88807);
}
/* check that iron is the first element */
if ((strcmp("Fe",Elt[2].label))!=0)
{
printf("(Taks=%d) first element (=%s) is not %s !!!\n\n",ThisTask,Elt[2].label,FIRST_ELEMENT);
endrun(88808);
}
}
int get_nelts()
{
return Cp->nelts;
}
float get_SolarAbundance(i)
{
return Elt[i+2].SolarAbundance;
}
char* get_Element(i)
{
return Elt[i+2].label;
}
double star_lifetime(double z,double m)
{
/* z is the mass fraction of metals, ie, the metallicity */
/* m is the stellar mass in code unit */
/* Return t in code time unit */
int i;
double a,b,c;
double coeff[3];
double logm,twologm,logm2,time;
/* convert m in msol */
m = m*All.UnitMass_in_g / SOLAR_MASS;
for (i=0;i<3;i++)
coeff[i] = ( Cp->coeff_z[i][0]*z+Cp->coeff_z[i][1] )*z+Cp->coeff_z[i][2];
a = coeff[0];
b = coeff[1];
c = coeff[2];
logm = log10(m);
twologm = 2.0 * logm;
logm2 = logm*logm;
time = pow(10.,(a*logm2+b*twologm+c));
return time;
}
double star_mass_from_age(double z,double t)
{
/* z is the mass fraction of metals, ie, the metallicity */
/* t is the star life time */
/* return the stellar mass (in code unit) that has a lifetime equal to t */
/* this is the inverse of star_lifetime */
int i;
double a,b,c;
double coeff[3];
double m;
for (i=0;i<3;i++)
coeff[i] = ( Cp->coeff_z[i][0]*z+Cp->coeff_z[i][1] )*z+Cp->coeff_z[i][2];
a = coeff[0];
b = coeff[1];
c = coeff[2];
m = -(b+sqrt(b*b-a*(c-log10(t))))/a;
m = pow(10,m); /* here, m is in solar mass */
m = m*SOLAR_MASS/All.UnitMass_in_g; /* Msol to mass unit */
return m;
}
/****************************************************************************************/
/*
/* Supernova rate : number of supernova per mass unit
/*
/****************************************************************************************/
double SNII_rate(double m1,double m2)
{
/*
masses in code unit
*/
double RSNII;
double md,mu;
RSNII = 0.0;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
m2 = m2*All.UnitMass_in_g / SOLAR_MASS;
/* (1) find md, mu */
md = dmax(m1,Cp->SNII_Mmin);
mu = dmin(m2,Cp->SNII_Mmax);
if (mu<=md) /* no SNII in that mass range */
return 0.0;
RSNII = Cp->SNII_cte * (pow(mu,Cp->SNII_a)-pow(md,Cp->SNII_a)); /* number per solar mass */
/* convert in number per solar mass to number per mass unit */
RSNII = RSNII *All.UnitMass_in_g / SOLAR_MASS;
return RSNII;
}
double SNIa_rate(double m1,double m2)
{
/*
masses in code unit
*/
double RSNIa;
double md,mu;
RSNIa = 0.0;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
m2 = m2*All.UnitMass_in_g / SOLAR_MASS;
/* RG contribution */
md = dmax(m1,Cp->SNIa_Mdl1);
mu = dmin(m2,Cp->SNIa_Mdu1);
if (md<mu)
RSNIa = RSNIa + Cp->SNIa_bb1 * Cp->SNIa_cte1 * (pow(mu,Cp->SNIa_a1)-pow(md,Cp->SNIa_a1));
/* MS contribution */
md = dmax(m1,Cp->SNIa_Mdl2);
mu = dmin(m2,Cp->SNIa_Mdu2);
if (md<mu)
RSNIa = RSNIa + Cp->SNIa_bb2 * Cp->SNIa_cte2 * (pow(mu,Cp->SNIa_a2)-pow(md,Cp->SNIa_a2));
/* WD contribution */
md = dmax(m1,Cp->SNIa_Mpl); /* select stars that have finished their life -> WD */
mu = Cp->SNIa_Mpu; /* no upper bond */
if (mu<=md) /* no SNIa in that mass range */
return 0.0;
RSNIa = RSNIa * Cp->SNIa_cte * (pow(mu,Cp->SNIa_a)-pow(md,Cp->SNIa_a)); /* number per solar mass */
/* convert in number per solar mass to number per mass unit */
RSNIa = RSNIa *All.UnitMass_in_g / SOLAR_MASS;
return RSNIa;
}
void SNII_mass_ejection(double m1,double m2)
{
double l1,l2;
int i1,i2,i1p,i2p,j;
double f1,f2;
double v1,v2;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
m2 = m2*All.UnitMass_in_g / SOLAR_MASS;
j = 0;
l1 = ( log10(m1) - Elt[j].Mmin) / Elt[j].Step ;
l2 = ( log10(m2) - Elt[j].Mmin) / Elt[j].Step ;
if (l1 < 0.0) l1 = 0.0;
if (l2 < 0.0) l2 = 0.0;
i1 = (int)l1;
i2 = (int)l2;
i1p = i1 + 1;
i2p = i2 + 1;
f1 = l1 - i1;
f2 = l2 - i2;
/* check (yr) */
if (i1<0) i1=0;
if (i2<0) i2=0;
/* --------- TOTAL GAS ---------- */
j = 0;
v1 = f1 * ( Elt[j].Array[i1p] - Elt[j].Array[i1] ) + Elt[j].Array[i1];
v2 = f2 * ( Elt[j].Array[i2p] - Elt[j].Array[i2] ) + Elt[j].Array[i2];
MassFracSNII[j] = v2-v1;
/* --------- He core therm ---------- */
j = 1;
v1 = f1 * ( Elt[j].Array[i1p] - Elt[j].Array[i1] ) + Elt[j].Array[i1];
v2 = f2 * ( Elt[j].Array[i2p] - Elt[j].Array[i2] ) + Elt[j].Array[i2];
MassFracSNII[j] = v2-v1;
/* ---------------------------- */
/* --------- Metals ---------- */
/* ---------------------------- */
j = 2;
l1 = ( log10(m1) - Elt[j].Mmin) / Elt[j].Step ;
l2 = ( log10(m2) - Elt[j].Mmin) / Elt[j].Step ;
if (l1 < 0.0) l1 = 0.0;
if (l2 < 0.0) l2 = 0.0;
i1 = (int)l1;
i2 = (int)l2;
i1p = i1 + 1;
i2p = i2 + 1;
f1 = l1 - i1;
f2 = l2 - i2;
/* check (yr) */
if (i1<0) i1=0;
if (i2<0) i2=0;
for (j=2;j<Cp->nelts+2;j++)
{
v1 = f1 * ( Elt[j].Array[i1p] - Elt[j].Array[i1] ) + Elt[j].Array[i1];
v2 = f2 * ( Elt[j].Array[i2p] - Elt[j].Array[i2] ) + Elt[j].Array[i2];
MassFracSNII[j] = v2-v1;
}
}
void SNII_single_mass_ejection(double m1)
{
double l1;
int i1,i1p,j;
double f1;
double v1;
/* convert m in msol */
m1 = m1*All.UnitMass_in_g / SOLAR_MASS;
j = 0;
l1 = ( log10(m1) - Elt[j].Mmin) / Elt[j].Step ;
if (l1 < 0.0) l1 = 0.0;
i1 = (int)l1;
i1p = i1 + 1;
f1 = l1 - i1;
/* check (yr) */
if (i1<0) i1=0;
/* --------- TOTAL GAS ---------- */
j = 0;
v1 = f1 * ( Elt[j].Metal[i1p] - Elt[j].Metal[i1] ) + Elt[j].Metal[i1];
SingleMassFracSNII[j] = v1;
/* --------- He core therm ---------- */
j = 1;
v1 = f1 * ( Elt[j].Metal[i1p] - Elt[j].Metal[i1] ) + Elt[j].Metal[i1];
SingleMassFracSNII[j] = v1;
/* ---------------------------- */
/* --------- Metals ---------- */
/* ---------------------------- */
j = 2;
l1 = ( log10(m1) - Elt[j].Mmin) / Elt[j].Step ;
if (l1 < 0.0) l1 = 0.0;
i1 = (int)l1;
i1p = i1 + 1;
f1 = l1 - i1;
/* check (yr) */
if (i1<0) i1=0;
for (j=2;j<Cp->nelts+2;j++)
{
v1 = f1 * ( Elt[j].Metal[i1p] - Elt[j].Metal[i1] ) + Elt[j].Metal[i1];
SingleMassFracSNII[j] = v1;
}
}
void Total_mass_ejection(double m1,double m2,double M0,double *z)
{
int j;
double NSNIa;
/* compute SNII mass ejection -> MassFracSNII */
SNII_mass_ejection(m1,m2);
/* number of SNIa per mass unit between time and time+dt */
NSNIa = SNIa_rate(m1,m2)*M0;
/* number of SNII per mass unit between time and time+dt */
//NSNII = SNII_rate(m1,m2)*M0; /* useless (only for energy) */
/* total ejected gas mass */
EjectedMass[0] = M0 * MassFracSNII[0] + Cp->Mco/All.UnitMass_in_g*SOLAR_MASS * NSNIa;
/* ejected mass per element */
for (j=2;j<Cp->nelts+2;j++)
EjectedMass[j] = M0*(MassFracSNII[j] +z[j-2]*MassFracSNII[1]) + NSNIa* Elt[j].MSNIa/All.UnitMass_in_g*SOLAR_MASS;
/* not used */
EjectedMass[1] = -1;
}
void Total_single_mass_ejection(double m1,double *z)
{
/*
!!! we do not take into account SNIa
*/
int j;
float M0;
M0 = m1;
/* compute SNII mass ejection -> SingleMassFracSNII */
SNII_single_mass_ejection(m1);
/* total ejected gas mass */
SingleEjectedMass[0] = M0 * SingleMassFracSNII[0]; /* + Cp->Mco/All.UnitMass_in_g*SOLAR_MASS * NSNIa; */
/* ejected mass per element */
for (j=2;j<Cp->nelts+2;j++)
SingleEjectedMass[j] = M0*(SingleMassFracSNII[j] +z[j-2]*SingleMassFracSNII[1]); /* + NSNIa* Elt[j].MSNIa/All.UnitMass_in_g*SOLAR_MASS; */
/* not used */
SingleEjectedMass[1] = -1;
}
/**********************************************************************************************
END OF CHIMIE FUNCTIONS
**********************************************************************************************/
#if defined(CHIMIE_THERMAL_FEEDBACK) && defined(CHIMIE_COMPUTE_THERMAL_FEEDBACK_ENERGY)
void chimie_compute_energy_int(int mode)
{
int i;
double DeltaEgyInt;
double Tot_DeltaEgyInt;
DeltaEgyInt = 0;
Tot_DeltaEgyInt = 0;
if (mode==1)
{
LocalSysState.EnergyInt1 = 0;
LocalSysState.EnergyInt2 = 0;
}
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
if (mode==1)
LocalSysState.EnergyInt1 += P[i].Mass * SphP[i].EntropyPred / (GAMMA_MINUS1) * pow(SphP[i].Density*a3inv, GAMMA_MINUS1);
else
LocalSysState.EnergyInt2 += P[i].Mass * SphP[i].EntropyPred / (GAMMA_MINUS1) * pow(SphP[i].Density*a3inv, GAMMA_MINUS1);
}
}
if (mode==2)
{
DeltaEgyInt = LocalSysState.EnergyInt2 - LocalSysState.EnergyInt1;
MPI_Reduce(&DeltaEgyInt, &Tot_DeltaEgyInt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
LocalSysState.EnergyThermalFeedback -= DeltaEgyInt;
}
}
#endif
#if defined(CHIMIE_KINETIC_FEEDBACK) && defined(CHIMIE_COMPUTE_KINETIC_FEEDBACK_ENERGY)
void chimie_compute_energy_kin(int mode)
{
int i;
double DeltaEgyKin;
double Tot_DeltaEgyKin;
DeltaEgyKin = 0;
Tot_DeltaEgyKin = 0;
if (mode==1)
{
LocalSysState.EnergyKin1 = 0;
LocalSysState.EnergyKin2 = 0;
}
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
if (mode==1)
LocalSysState.EnergyKin1 += 0.5 * P[i].Mass * (P[i].Vel[0]*P[i].Vel[0]+P[i].Vel[1]*P[i].Vel[1]+P[i].Vel[2]*P[i].Vel[2]);
else
LocalSysState.EnergyKin2 += 0.5 * P[i].Mass * (P[i].Vel[0]*P[i].Vel[0]+P[i].Vel[1]*P[i].Vel[1]+P[i].Vel[2]*P[i].Vel[2]);
}
}
if (mode==2)
{
DeltaEgyKin = LocalSysState.EnergyKin2 - LocalSysState.EnergyKin1;
MPI_Reduce(&DeltaEgyKin, &Tot_DeltaEgyKin, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
LocalSysState.EnergyKineticFeedback -= DeltaEgyKin;
}
}
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
void chimie_apply_thermal_feedback(void)
{
int i;
double EgySpec,NewEgySpec,DeltaEntropy;
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
if (SphP[i].DeltaEgySpec > 0)
{
/* spec energy at current step */
EgySpec = SphP[i].EntropyPred / GAMMA_MINUS1 * pow(SphP[i].Density*a3inv, GAMMA_MINUS1);
/* new egyspec */
NewEgySpec = EgySpec + SphP[i].DeltaEgySpec;
LocalSysState.EnergyThermalFeedback -= SphP[i].DeltaEgySpec*P[i].Mass;
/* new entropy */
DeltaEntropy = GAMMA_MINUS1*NewEgySpec/pow(SphP[i].Density*a3inv, GAMMA_MINUS1) - SphP[i].EntropyPred;
SphP[i].EntropyPred += DeltaEntropy;
SphP[i].Entropy += DeltaEntropy;
/* set the adiabatic period for SNIa */
if (SphP[i].NumberOfSNIa>0)
SphP[i].SNIaThermalTime = All.Time;
/* set the adiabatic period for SNII */
if (SphP[i].NumberOfSNII>0)
SphP[i].SNIIThermalTime = All.Time;
/* reset variables */
SphP[i].DeltaEgySpec = 0;
SphP[i].NumberOfSNIa = 0;
SphP[i].NumberOfSNII = 0;
}
}
}
}
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
void chimie_apply_wind(void)
{
/* apply wind */
int i;
double e1,e2;
double phi,costh,sinth,vx,vy,vz;
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
if (SphP[i].WindFlag)
{
phi = get_ChimieKineticFeedback_random_number(P[i].ID)*PI*2.;
costh = 1.-2.*get_ChimieKineticFeedback_random_number(P[i].ID+1);
sinth = sqrt(1.-pow(costh,2));
vx = All.ChimieWindSpeed*sinth*cos(phi);
vy = All.ChimieWindSpeed*sinth*sin(phi);
vz = All.ChimieWindSpeed*costh;
e1 = 0.5*P[i].Mass * ( SphP[i].VelPred[0]*SphP[i].VelPred[0] + SphP[i].VelPred[1]*SphP[i].VelPred[1] + SphP[i].VelPred[2]*SphP[i].VelPred[2]);
P[i].Vel[0] += vx;
P[i].Vel[1] += vy;
P[i].Vel[2] += vz;
SphP[i].VelPred[0] += vx;
SphP[i].VelPred[1] += vy;
SphP[i].VelPred[2] += vz;
e2 = 0.5*P[i].Mass * ( SphP[i].VelPred[0]*SphP[i].VelPred[0] + SphP[i].VelPred[1]*SphP[i].VelPred[1] + SphP[i].VelPred[2]*SphP[i].VelPred[2]);
LocalSysState.EnergyKineticFeedback -= e2-e1;
SphP[i].WindFlag = 0;
}
}
}
}
#endif
/*! This function is the driver routine for the calculation of chemical evolution
*/
void chimie(void)
{
double t0, t1;
t0 = second(); /* measure the time for the full chimie computation */
if (ThisTask==0)
printf("Start Chimie computation.\n");
if(All.ComovingIntegrationOn)
{
/* Factors for comoving integration of hydro */
hubble_a = All.Omega0 / (All.Time * All.Time * All.Time)
+ (1 - All.Omega0 - All.OmegaLambda) / (All.Time * All.Time) + All.OmegaLambda;
hubble_a = All.Hubble * sqrt(hubble_a);
hubble_a2 = All.Time * All.Time * hubble_a;
fac_mu = pow(All.Time, 3 * (GAMMA - 1) / 2) / All.Time;
fac_egy = pow(All.Time, 3 * (GAMMA - 1));
fac_vsic_fix = hubble_a * pow(All.Time, 3 * GAMMA_MINUS1);
a3inv = 1 / (All.Time * All.Time * All.Time);
atime = All.Time;
#ifdef FEEDBACK
fac_pow = fac_egy*atime*atime;
#endif
}
else
{
hubble_a = hubble_a2 = atime = fac_mu = fac_vsic_fix = a3inv = fac_egy = 1.0;
#ifdef FEEDBACK
fac_pow = 1.0;
#endif
}
/* apply thermal feedback on selected particles */
#ifdef CHIMIE_THERMAL_FEEDBACK
chimie_apply_thermal_feedback();
#endif
/* apply wind on selected particles */
#ifdef CHIMIE_KINETIC_FEEDBACK
chimie_apply_wind();
#endif
stars_density(); /* compute density */
do_chimie(); /* chimie */
if (ThisTask==0)
printf("Chimie computation done.\n");
t1 = second();
All.CPU_Chimie += timediff(t0, t1);
}
/*! This function is the driver routine for the calculation of chemical evolution
*/
void do_chimie(void)
{
long long ntot, ntotleft;
int i, j, k, n, m, ngrp, maxfill, source, ndone;
int *nbuffer, *noffset, *nsend_local, *nsend, *numlist, *ndonelist;
int level, sendTask, recvTask, nexport, place;
double tstart, tend, sumt, sumcomm;
double timecomp = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance;
int flag_chimie;
MPI_Status status;
int do_it;
int Ti0,Ti1,Ti2;
double t1,t2,t01,t02;
double tmin,tmax;
double minlivetime,maxlivetime;
double m1,m2,M0;
double NSNIa,NSNII;
double NSNIa_tot,NSNII_tot,NSNIa_totlocal,NSNII_totlocal;
double EgySN,EgySNlocal;
double EgySNThermal,EgySNKinetic;
int Nchim,Nchimlocal;
int Nwind,Nwindlocal;
int Nflag,Nflaglocal;
int Noldwind,Noldwindlocal;
double metals[NELEMENTS];
double FeH;
float MinRelMass=1e-3;
+#ifdef DETAILED_CPU_OUTPUT_IN_CHIMIE
+ double *timecomplist;
+ double *timecommsummlist;
+ double *timeimbalancelist;
+#endif
+
+
#ifdef PERIODIC
boxSize = All.BoxSize;
boxHalf = 0.5 * All.BoxSize;
#ifdef LONG_X
boxHalf_X = boxHalf * LONG_X;
boxSize_X = boxSize * LONG_X;
#endif
#ifdef LONG_Y
boxHalf_Y = boxHalf * LONG_Y;
boxSize_Y = boxSize * LONG_Y;
#endif
#ifdef LONG_Z
boxHalf_Z = boxHalf * LONG_Z;
boxSize_Z = boxSize * LONG_Z;
#endif
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
double v1m,v2m;
#endif
/* `NumStUpdate' gives the number of particles on this processor that want a chimie computation */
for(n = 0, NumStUpdate = 0; n < N_gas+N_stars; n++)
{
if(P[n].Ti_endstep == All.Ti_Current)
if(P[n].Type == ST)
{
m = P[n].StPIdx;
if ( (P[n].Mass/StP[m].InitialMass) > MinRelMass)
NumStUpdate++;
}
if(P[n].Type == 0)
SphP[n].dMass = 0.;
}
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&NumStUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */
nbuffer = malloc(sizeof(int) * NTask);
nsend_local = malloc(sizeof(int) * NTask);
nsend = malloc(sizeof(int) * NTask * NTask);
ndonelist = malloc(sizeof(int) * NTask);
i = 0; /* first gas particle, because stars may be hidden among gas particles */
ntotleft = ntot; /* particles left for all tasks together */
NSNIa_tot = 0;
NSNII_tot = 0;
NSNIa_totlocal = 0;
NSNII_totlocal = 0;
EgySN = 0;
EgySNlocal =0;
Nchimlocal = 0;
Nchim = 0;
Nwindlocal = 0;
Nwind = 0;
Noldwindlocal = 0;
Noldwind = 0;
Nflaglocal = 0;
Nflag = 0;
while(ntotleft > 0)
{
for(j = 0; j < NTask; j++)
nsend_local[j] = 0;
/* do local particles and prepare export list */
tstart = second();
for(nexport = 0, ndone = 0; i < N_gas+N_stars && nexport < All.BunchSizeChimie - NTask; i++)
{
/* only active particles and stars */
if((P[i].Ti_endstep == All.Ti_Current)&&(P[i].Type == ST))
{
if(P[i].Type != ST)
{
printf("P[i].Type != ST, we better stop.\n");
printf("N_gas=%d (type=%d) i=%d (type=%d)\n",N_gas,P[N_gas].Type,i,P[i].Type);
printf("Please, check that you do not use PEANOHILBERT\n");
endrun(777001);
}
m = P[i].StPIdx;
if ( (P[i].Mass/StP[m].InitialMass) > MinRelMass)
{
flag_chimie = 0;
/******************************************/
/* do chimie */
/******************************************/
/*****************************************************/
/* look if a SN may have explode during the last step
/*****************************************************/
/***********************************************/
/***********************************************/
/* set the right table base of the metallicity */
set_table(0);
//FeH = log10( (StP[m].Metal[FE]/get_SolarAbundance(FE)) + 1.e-20 );
//if (FeH<-3)
// set_table(1);
//else
// set_table(0);
//if (P[i].ID==65546)
// {
// printf("(%d) %g the particle 65546 FeH=%g metalFe=%g Mmin=%g Mmax=%g n=%d\n",ThisTask,All.Time,FeH,StP[m].Metal[FE],Cp->Mmin,Cp->Mmax,Cp->n);
// }
/*
Cp->Mmin
Cp->Mmax
Cp->n
Cp->ms[]
Cp->as[]
Cp->SNIa_cte
Cp->SNIa_a
Cp->SNIa_Mdl1
Cp->SNIa_Mdu1
Cp->SNIa_bb1
Cp->SNIa_cte1
Cp->SNIa_a1
Cp->SNIa_Mdl2
Cp->SNIa_Mdu2
Cp->SNIa_bb2
Cp->SNIa_cte2
Cp->SNIa_a2
*/
/***********************************************/
/***********************************************/
/* minimum live time for a given metallicity */
minlivetime = star_lifetime(StP[m].Metal[NELEMENTS-1],Cp->Mmax*SOLAR_MASS/All.UnitMass_in_g)*All.HubbleParam;
/* maximum live time for a given metallicity */
maxlivetime = star_lifetime(StP[m].Metal[NELEMENTS-1],Cp->Mmin*SOLAR_MASS/All.UnitMass_in_g)*All.HubbleParam;
//if (P[i].ID==65546)
// printf("(%d) %g the particle 65546 has a max livetime of %g (metal=%g Mmin=%g)\n",ThisTask,All.Time,maxlivetime,StP[m].Metal[NELEMENTS-1],Cp->Mmin);
if (All.ComovingIntegrationOn)
{
/* FormationTime on the time line */
Ti0 = log(StP[m].FormationTime/All.TimeBegin) / All.Timebase_interval;
/* Beginning of time step on the time line */
Ti1 = P[i].Ti_begstep;
/* End of time step on the time line */
Ti2 = All.Ti_Current;
#ifdef COSMICTIME
t01 = get_cosmictime_difference(Ti0,Ti1);
t02 = get_cosmictime_difference(Ti0,Ti2);
#endif
}
else
{
t1 = All.TimeBegin + (P[i].Ti_begstep * All.Timebase_interval);
t2 = All.TimeBegin + (All.Ti_Current * All.Timebase_interval);
t01 = t1-StP[m].FormationTime;
t02 = t2-StP[m].FormationTime;
}
/* now treat all cases */
do_it=1;
/* beginning of interval */
if (t01>=minlivetime)
if (t01>=maxlivetime)
do_it=0; /* nothing to do */
else
m2 = star_mass_from_age(StP[m].Metal[NELEMENTS-1],t01/All.HubbleParam)*All.HubbleParam;
else
m2 = Cp->Mmax*SOLAR_MASS/All.UnitMass_in_g*All.HubbleParam;
/* end of interval */
if (t02<=maxlivetime)
if (t02<=minlivetime)
do_it=0; /* nothing to do */
else
m1 = star_mass_from_age(StP[m].Metal[NELEMENTS-1],t02/All.HubbleParam)*All.HubbleParam;
else
m1 = Cp->Mmin*SOLAR_MASS/All.UnitMass_in_g*All.HubbleParam;
//printf("Time=%g t01=%g t02=%g id=%d minlivetime=%g maxlivetime=%g \n",All.Time,t01,t02,P[i].ID,minlivetime,maxlivetime);
/* if some of the stars in the SSP explode between t1 and t2 */
if (do_it)
{
Nchimlocal++;
StP[m].Flag = 1; /* mark it as active */
if (m1>m2)
{
printf("m1=%g (%g Msol) > m2=%g (%g Msol) !!!\n\n",m1,m1*All.UnitMass_in_g/SOLAR_MASS,m2,m2*All.UnitMass_in_g/SOLAR_MASS);
endrun(777002);
}
M0 = StP[m].InitialMass;
for (k=0;k<NELEMENTS;k++)
metals[k] = StP[m].Metal[k];
/* number of SNIa */
NSNIa = SNIa_rate(m1/All.HubbleParam,m2/All.HubbleParam)*M0/All.HubbleParam;
/* number of SNII */
NSNII = SNII_rate(m1/All.HubbleParam,m2/All.HubbleParam)*M0/All.HubbleParam;
/* discretize SN */
#ifdef CHIMIE_MC_SUPERNOVAE
double fNSNIa,fNSNII;
fNSNIa = NSNIa-floor(NSNIa);
NSNIa = floor(NSNIa);
if (get_Chimie_random_number(P[i].ID) < fNSNIa)
NSNIa = NSNIa+1;
fNSNII = NSNII-floor(NSNII);
NSNII = floor(NSNII);
if (get_Chimie_random_number(P[i].ID) < fNSNII)
NSNII = NSNII+1;
#endif CHIMIE_MC_SUPERNOVAE
/* discretize SN */
NSNIa_totlocal += NSNIa;
NSNII_totlocal += NSNII;
/* compute ejectas */
Total_mass_ejection(m1/All.HubbleParam,m2/All.HubbleParam,M0/All.HubbleParam,metals);
StP[m].TotalEjectedGasMass = EjectedMass[0]*All.HubbleParam; /* gas mass */
for (k=0;k<NELEMENTS;k++)
StP[m].TotalEjectedEltMass[k] = EjectedMass[k+2]*All.HubbleParam; /* metal mass */
if (StP[m].TotalEjectedGasMass>0)
flag_chimie=1;
/* compute injected energy */
StP[m].TotalEjectedEgySpec = All.ChimieSupernovaEnergy* (NSNIa + NSNII) /StP[m].TotalEjectedGasMass;
StP[m].NumberOfSNIa = NSNIa;
StP[m].NumberOfSNII = NSNII;
EgySNlocal += All.ChimieSupernovaEnergy* (NSNIa + NSNII);
/* correct mass particle */
if (P[i].Mass-StP[m].TotalEjectedGasMass<0)
{
printf("mass wants to be less than zero...\n");
printf("P[i].Mass=%g StP[m].TotalEjectedGasMass=%g\n",P[i].Mass,StP[m].TotalEjectedGasMass);
endrun(777100);
}
//if (P[i].ID==65546)
// printf("(%d) %g the particle 65546 is here, mass=%g TotalEjectedEltMass=%g m1=%g m2=%g\n",ThisTask,All.Time,P[i].Mass,StP[m].TotalEjectedGasMass,m1,m2);
P[i].Mass = P[i].Mass-StP[m].TotalEjectedGasMass;
//float Fe,Mg;
//Fe = StP[m].TotalEjectedEltMass[0];
//Mg = StP[m].TotalEjectedEltMass[1];
}
/******************************************/
/* end do chimie */
/******************************************/
ndone++;
if (flag_chimie)
{
for(j = 0; j < NTask; j++)
Exportflag[j] = 0;
chimie_evaluate(i, 0);
for(j = 0; j < NTask; j++)
{
if(Exportflag[j])
{
for(k = 0; k < 3; k++)
{
ChimieDataIn[nexport].Pos[k] = P[i].Pos[k];
ChimieDataIn[nexport].Vel[k] = P[i].Vel[k];
}
ChimieDataIn[nexport].ID = P[i].ID;
ChimieDataIn[nexport].Timestep = P[i].Ti_endstep - P[i].Ti_begstep;
ChimieDataIn[nexport].Hsml = StP[m].Hsml;
ChimieDataIn[nexport].Density = StP[m].Density;
ChimieDataIn[nexport].Volume = StP[m].Volume;
#ifdef CHIMIE_KINETIC_FEEDBACK
ChimieDataIn[nexport].NgbMass = StP[m].NgbMass;
#endif
ChimieDataIn[nexport].TotalEjectedGasMass = StP[m].TotalEjectedGasMass;
for(k = 0; k < NELEMENTS; k++)
ChimieDataIn[nexport].TotalEjectedEltMass[k] = StP[m].TotalEjectedEltMass[k];
ChimieDataIn[nexport].TotalEjectedEgySpec = StP[m].TotalEjectedEgySpec;
ChimieDataIn[nexport].NumberOfSNIa = StP[m].NumberOfSNIa;
ChimieDataIn[nexport].NumberOfSNII = StP[m].NumberOfSNII;
#ifdef WITH_ID_IN_HYDRA
ChimieDataIn[nexport].ID = P[i].ID;
#endif
ChimieDataIn[nexport].Index = i;
ChimieDataIn[nexport].Task = j;
nexport++;
nsend_local[j]++;
}
}
}
}
}
}
tend = second();
timecomp += timediff(tstart, tend);
qsort(ChimieDataIn, nexport, sizeof(struct chimiedata_in), chimie_compare_key);
for(j = 1, noffset[0] = 0; j < NTask; j++)
noffset[j] = noffset[j - 1] + nsend_local[j - 1];
tstart = second();
MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* now do the particles that need to be exported */
for(level = 1; level < (1 << PTask); level++)
{
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeChimie)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* get the particles */
MPI_Sendrecv(&ChimieDataIn[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct chimiedata_in), MPI_BYTE,
recvTask, TAG_CHIMIE_A,
&ChimieDataGet[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct chimiedata_in), MPI_BYTE,
recvTask, TAG_CHIMIE_A, MPI_COMM_WORLD, &status);
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
/* now do the imported particles */
tstart = second();
for(j = 0; j < nbuffer[ThisTask]; j++)
chimie_evaluate(j, 1);
tend = second();
timecomp += timediff(tstart, tend);
/* do a block to measure imbalance */
tstart = second();
MPI_Barrier(MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* get the result */
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeChimie)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* send the results */
MPI_Sendrecv(&ChimieDataResult[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct chimiedata_out),
MPI_BYTE, recvTask, TAG_CHIMIE_B,
&ChimieDataPartialResult[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct chimiedata_out),
MPI_BYTE, recvTask, TAG_CHIMIE_B, MPI_COMM_WORLD, &status);
/* add the result to the particles */
for(j = 0; j < nsend_local[recvTask]; j++)
{
source = j + noffset[recvTask];
place = ChimieDataIn[source].Index;
// for(k = 0; k < 3; k++)
// SphP[place].HydroAccel[k] += HydroDataPartialResult[source].Acc[k];
//
// SphP[place].DtEntropy += HydroDataPartialResult[source].DtEntropy;
//#ifdef FEEDBACK
// SphP[place].DtEgySpecFeedback += HydroDataPartialResult[source].DtEgySpecFeedback;
//#endif
// if(SphP[place].MaxSignalVel < HydroDataPartialResult[source].MaxSignalVel)
// SphP[place].MaxSignalVel = HydroDataPartialResult[source].MaxSignalVel;
//#ifdef COMPUTE_VELOCITY_DISPERSION
// for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
// SphP[place].VelocityDispersion[k] += HydroDataPartialResult[source].VelocityDispersion[k];
//#endif
}
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
level = ngrp - 1;
}
MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD);
for(j = 0; j < NTask; j++)
ntotleft -= ndonelist[j];
}
free(ndonelist);
free(nsend);
free(nsend_local);
free(nbuffer);
free(noffset);
/* do final operations on results */
tstart = second();
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
P[i].Mass += SphP[i].dMass;
SphP[i].dMass = 0.;
}
}
tend = second();
timecomp += timediff(tstart, tend);
/* collect some timing information */
MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
-// if(ThisTask == 0)
-// {
-// All.CPU_HydCompWalk += sumt / NTask;
-// All.CPU_HydCommSumm += sumcomm / NTask;
-// All.CPU_HydImbalance += sumimbalance / NTask;
-// }
+
+
+ if(ThisTask == 0)
+ {
+ All.CPU_ChimieCompWalk += sumt / NTask;
+ All.CPU_ChimieCommSumm += sumcomm / NTask;
+ All.CPU_ChimieImbalance += sumimbalance / NTask;
+ }
+
+
+
+
+
+#ifdef DETAILED_CPU_OUTPUT_IN_CHIMIE
+ numlist = malloc(sizeof(int) * NTask);
+ timecomplist = malloc(sizeof(double) * NTask);
+ timecommsummlist = malloc(sizeof(double) * NTask);
+ timeimbalancelist = malloc(sizeof(double) * NTask);
+
+ MPI_Gather(&NumStUpdate, 1, MPI_INT, numlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecomp, 1, MPI_DOUBLE, timecomplist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecommsumm, 1, MPI_DOUBLE, timecommsummlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timeimbalance, 1, MPI_DOUBLE, timeimbalancelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+
+ if(ThisTask == 0)
+ {
+ fprintf(FdTimings, "\n chimie\n\n");
+
+ fprintf(FdTimings, "Nupdate ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12d ",numlist[i]); /* nombre de part par proc */
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecomp ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecomplist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecommsumm ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecommsummlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timeimbalance ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timeimbalancelist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "\n");
+ }
+
+ free(timeimbalancelist);
+ free(timecommsummlist);
+ free(timecomplist);
+ free(numlist);
+#endif
+
+
/* collect some chimie informations */
MPI_Reduce(&NSNIa_totlocal, &NSNIa_tot, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&NSNII_totlocal, &NSNII_tot, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&EgySNlocal, &EgySN, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&Nchimlocal, &Nchim, 1, MPI_INT , MPI_SUM, 0, MPI_COMM_WORLD);
-
+
#ifdef CHIMIE_THERMAL_FEEDBACK
EgySNThermal = EgySN*(1-All.ChimieKineticFeedbackFraction);
#else
EgySNThermal = 0;
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
EgySNKinetic = EgySN*All.ChimieKineticFeedbackFraction;
/* count number of wind particles */
for(i = 0; i < N_gas; i++)
{
if (P[i].Type==0)
{
if (SphP[i].WindTime >= (All.Time-All.ChimieWindTime))
Nwindlocal++;
//else
// if (SphP[i].WindTime > All.TimeBegin-2*All.ChimieWindTime)
// Noldwindlocal++;
if (SphP[i].WindFlag)
Nflaglocal++;
}
}
MPI_Reduce(&Nwindlocal, &Nwind, 1, MPI_INT , MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&Noldwindlocal, &Noldwind, 1, MPI_INT , MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Allreduce(&Nflaglocal, &Nflag, 1, MPI_INT , MPI_SUM, MPI_COMM_WORLD);
#else
EgySNKinetic = 0;
#endif
/* write some info */
if (ThisTask==0)
{
fprintf(FdChimie, "%15g %10d %15g %15g %15g %15g %15g %10d %10d %10d\n",All.Time,Nchim,NSNIa_tot,NSNII_tot,EgySN,EgySNThermal,EgySNKinetic,Nwind,Noldwind,Nflag);
fflush(FdChimie);
}
if (Nflag>0)
{
SetMinTimeStepForActives=1;
if (ThisTask==0)
fprintf(FdLog,"%g : !!! set min timestep for active particles !!!\n",All.Time);
}
}
/*! This function is the 'core' of the Chemie computation. A target
* particle is specified which may either be local, or reside in the
* communication buffer.
*/
void chimie_evaluate(int target, int mode)
{
int j, n, startnode, numngb,numngb_inbox,k;
FLOAT *pos,*vel;
//FLOAT *vel;
//FLOAT mass;
double h, h2;
double acc[3];
double dx, dy, dz;
double wk, r, r2, u=0;
double hinv=1, hinv3;
int target_stp;
double density;
double volume;
#ifdef CHIMIE_KINETIC_FEEDBACK
double ngbmass;
double p;
#endif
double aij;
double ejectedGasMass;
double ejectedEltMass[NELEMENTS];
double ejectedEgySpec;
double NumberOfSNIa;
double NumberOfSNII;
double mass_k;
double NewMass;
double fv,vi2,vj2;
double EgySpec,NewEgySpec;
double DeltaEntropy;
double DeltaVel[3];
#ifndef LONGIDS
unsigned int id; /*!< particle identifier */
#else
unsigned long long id; /*!< particle identifier */
#endif
if(mode == 0)
{
pos = P[target].Pos;
vel = P[target].Vel;
id = P[target].ID;
target_stp = P[target].StPIdx;
h = StP[target_stp].Hsml;
density = StP[target_stp].Density;
volume = StP[target_stp].Volume;
#ifdef CHIMIE_KINETIC_FEEDBACK
ngbmass = StP[target_stp].NgbMass;
#endif
ejectedGasMass = StP[target_stp].TotalEjectedGasMass;
for(k=0;k<NELEMENTS;k++)
ejectedEltMass[k] = StP[target_stp].TotalEjectedEltMass[k];
ejectedEgySpec = StP[target_stp].TotalEjectedEgySpec;
NumberOfSNIa = StP[target_stp].NumberOfSNIa;
NumberOfSNII = StP[target_stp].NumberOfSNII;
}
else
{
pos = ChimieDataGet[target].Pos;
vel = ChimieDataGet[target].Vel;
id = ChimieDataGet[target].ID;
h = ChimieDataGet[target].Hsml;
density = ChimieDataGet[target].Density;
volume = ChimieDataGet[target].Volume;
#ifdef CHIMIE_KINETIC_FEEDBACK
ngbmass = ChimieDataGet[target].NgbMass;
#endif
ejectedGasMass = ChimieDataGet[target].TotalEjectedGasMass;
for(k=0;k<NELEMENTS;k++)
ejectedEltMass[k] = ChimieDataGet[target].TotalEjectedEltMass[k];
ejectedEgySpec = ChimieDataGet[target].TotalEjectedEgySpec;
NumberOfSNIa = ChimieDataGet[target].NumberOfSNIa;
NumberOfSNII = ChimieDataGet[target].NumberOfSNII;
}
/* initialize variables before SPH loop is started */
acc[0] = acc[1] = acc[2] = 0;
vi2 = 0;
for(k=0;k<3;k++)
vi2 += vel[k]*vel[k];
h2 = h * h;
hinv = 1.0 / h;
#ifndef TWODIMS
hinv3 = hinv * hinv * hinv;
#else
hinv3 = hinv * hinv / boxSize_Z;
#endif
/* Now start the actual SPH computation for this particle */
startnode = All.MaxPart;
numngb = 0;
do
{
numngb_inbox = ngb_treefind_variable_for_chimie(&pos[0], h, &startnode);
for(n = 0; n < numngb_inbox; n++)
{
j = Ngblist[n];
dx = pos[0] - P[j].Pos[0];
dy = pos[1] - P[j].Pos[1];
dz = pos[2] - P[j].Pos[2];
#ifdef PERIODIC /* now find the closest image in the given box size */
if(dx > boxHalf_X)
dx -= boxSize_X;
if(dx < -boxHalf_X)
dx += boxSize_X;
if(dy > boxHalf_Y)
dy -= boxSize_Y;
if(dy < -boxHalf_Y)
dy += boxSize_Y;
if(dz > boxHalf_Z)
dz -= boxSize_Z;
if(dz < -boxHalf_Z)
dz += boxSize_Z;
#endif
r2 = dx * dx + dy * dy + dz * dz;
if(r2 < h2)
{
numngb++;
r = sqrt(r2);
u = r * hinv;
if(u < 0.5)
{
wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u);
}
else
{
wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u);
}
/* normalisation using mass */
aij = P[j].Mass*wk/density;
/* normalisation using volume */
/* !!! si on utilise, il faut stoquer une nouvelle variable : OldDensity, car density est modifié plus bas... */
//aij = P[j].Mass/SphP[j].Density*wk/volume;
/* metal injection */
for(k=0;k<NELEMENTS;k++)
{
mass_k = SphP[j].Metal[k]*P[j].Mass; /* mass of elt k */
SphP[j].Metal[k] = ( mass_k + aij*ejectedEltMass[k] )/( P[j].Mass + aij*ejectedGasMass );
}
/* new mass */
NewMass = P[j].Mass + aij*ejectedGasMass;
/* new velocity */
vj2 = 0;
for(k=0;k<3;k++)
vj2 += SphP[j].VelPred[k]*SphP[j].VelPred[k];
fv = sqrt( (P[j].Mass/NewMass) + aij*(ejectedGasMass/NewMass) * (vi2/vj2) );
for(k=0;k<3;k++)
{
DeltaVel[k] = fv*SphP[j].VelPred[k] - SphP[j].VelPred[k];
SphP[j].VelPred[k] += DeltaVel[k];
P[j].Vel [k] += DeltaVel[k];
}
/* spec energy at current step */
EgySpec = SphP[j].EntropyPred / GAMMA_MINUS1 * pow(SphP[j].Density*a3inv, GAMMA_MINUS1);
/* new egyspec */
NewEgySpec = (EgySpec )*(P[j].Mass/NewMass);
/* new density */
SphP[j].Density = SphP[j].Density*NewMass/P[j].Mass;
/* new entropy */
DeltaEntropy = GAMMA_MINUS1*NewEgySpec/pow(SphP[j].Density*a3inv, GAMMA_MINUS1) - SphP[j].EntropyPred;
SphP[j].EntropyPred += DeltaEntropy;
SphP[j].Entropy += DeltaEntropy;
#ifdef CHIMIE_THERMAL_FEEDBACK
SphP[j].DeltaEgySpec += (1.-All.ChimieKineticFeedbackFraction)*(ejectedGasMass*ejectedEgySpec)* aij/NewMass;
SphP[j].NumberOfSNII += NumberOfSNII*aij;
SphP[j].NumberOfSNIa += NumberOfSNIa*aij;
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
p = (All.ChimieKineticFeedbackFraction*ejectedEgySpec*ejectedGasMass)/(0.5*ngbmass*All.ChimieWindSpeed*All.ChimieWindSpeed);
double r;
r = get_Chimie_random_number(P[j].ID+id);
if ( r < p) /* we should maybe have a 2d table here... */
{
if (SphP[j].WindTime < (All.Time-All.ChimieWindTime)) /* not a wind particle */
{
SphP[j].WindFlag = 1;
SphP[j].WindTime = All.Time;
}
}
#endif
#ifdef CHECK_ENTROPY_SIGN
if ((SphP[j].EntropyPred < 0)||(SphP[j].Entropy < 0))
{
printf("\ntask=%d: entropy less than zero in chimie_evaluate !\n", ThisTask);
printf("ID=%d Entropy=%g EntropyPred=%g DeltaEntropy=%g\n",P[j].ID,SphP[j].Entropy,SphP[j].EntropyPred,DeltaEntropy);
fflush(stdout);
endrun(777003);
}
#endif
/* store mass diff. */
SphP[j].dMass += NewMass-P[j].Mass;
}
}
}
while(startnode >= 0);
/* Now collect the result at the right place */
if(mode == 0)
{
// for(k = 0; k < 3; k++)
// SphP[target].HydroAccel[k] = acc[k];
// SphP[target].DtEntropy = dtEntropy;
//#ifdef FEEDBACK
// SphP[target].DtEgySpecFeedback = dtEgySpecFeedback;
//#endif
// SphP[target].MaxSignalVel = maxSignalVel;
//#ifdef COMPUTE_VELOCITY_DISPERSION
// for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
// SphP[target].VelocityDispersion[k] = VelocityDispersion[k];
//#endif
}
else
{
// for(k = 0; k < 3; k++)
// HydroDataResult[target].Acc[k] = acc[k];
// HydroDataResult[target].DtEntropy = dtEntropy;
//#ifdef FEEDBACK
// HydroDataResult[target].DtEgySpecFeedback = dtEgySpecFeedback;
//#endif
// HydroDataResult[target].MaxSignalVel = maxSignalVel;
//#ifdef COMPUTE_VELOCITY_DISPERSION
// for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
// HydroDataResult[target].VelocityDispersion[k] = VelocityDispersion[k];
//#endif
}
}
/*! This is a comparison kernel for a sort routine, which is used to group
* particles that are going to be exported to the same CPU.
*/
int chimie_compare_key(const void *a, const void *b)
{
if(((struct chimiedata_in *) a)->Task < (((struct chimiedata_in *) b)->Task))
return -1;
if(((struct chimiedata_in *) a)->Task > (((struct chimiedata_in *) b)->Task))
return +1;
return 0;
}
#endif
diff --git a/density.c b/density.c
index ae34d6c..5b91e5a 100644
--- a/density.c
+++ b/density.c
@@ -1,750 +1,809 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include "allvars.h"
#include "proto.h"
/*! \file density.c
* \brief SPH density computation and smoothing length determination
*
* This file contains the "first SPH loop", where the SPH densities and
* some auxiliary quantities are computed. If the number of neighbours
* obtained falls outside the target range, the correct smoothing
* length is determined iteratively, if needed.
*/
#ifdef PERIODIC
static double boxSize, boxHalf;
#ifdef LONG_X
static double boxSize_X, boxHalf_X;
#else
#define boxSize_X boxSize
#define boxHalf_X boxHalf
#endif
#ifdef LONG_Y
static double boxSize_Y, boxHalf_Y;
#else
#define boxSize_Y boxSize
#define boxHalf_Y boxHalf
#endif
#ifdef LONG_Z
static double boxSize_Z, boxHalf_Z;
#else
#define boxSize_Z boxSize
#define boxHalf_Z boxHalf
#endif
#endif
/*! This function computes the local density for each active SPH particle,
* the number of neighbours in the current smoothing radius, and the
* divergence and curl of the velocity field. The pressure is updated as
* well. If a particle with its smoothing region is fully inside the
* local domain, it is not exported to the other processors. The function
* also detects particles that have a number of neighbours outside the
* allowed tolerance range. For these particles, the smoothing length is
* adjusted accordingly, and the density computation is executed again.
* Note that the smoothing length is not allowed to fall below the lower
* bound set by MinGasHsml.
*/
void density(int mode)
{
long long ntot, ntotleft;
int *noffset, *nbuffer, *nsend, *nsend_local, *numlist, *ndonelist;
int i, j, n, ndone, npleft, maxfill, source, iter = 0;
int level, ngrp, sendTask, recvTask, place, nexport;
double dt_entr, tstart, tend, tstart_ngb = 0, tend_ngb = 0;
double sumt, sumcomm, timengb, sumtimengb;
double timecomp = 0, timeimbalance = 0, timecommsumm = 0, sumimbalance;
MPI_Status status;
+
+#ifdef DETAILED_CPU_OUTPUT_IN_DENSITY
+ double *timengblist;
+ double *timecomplist;
+ double *timecommsummlist;
+ double *timeimbalancelist;
+#endif
+
#ifdef DETAILED_CPU
double t0=0,t1=0;
#endif
#ifdef PERIODIC
boxSize = All.BoxSize;
boxHalf = 0.5 * All.BoxSize;
#ifdef LONG_X
boxHalf_X = boxHalf * LONG_X;
boxSize_X = boxSize * LONG_X;
#endif
#ifdef LONG_Y
boxHalf_Y = boxHalf * LONG_Y;
boxSize_Y = boxSize * LONG_Y;
#endif
#ifdef LONG_Z
boxHalf_Z = boxHalf * LONG_Z;
boxSize_Z = boxSize * LONG_Z;
#endif
#endif
#ifdef DETAILED_CPU
if (mode==1)
t0 = second();
#endif
noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */
nbuffer = malloc(sizeof(int) * NTask);
nsend_local = malloc(sizeof(int) * NTask);
nsend = malloc(sizeof(int) * NTask * NTask);
ndonelist = malloc(sizeof(int) * NTask);
-
+
for(n = 0, NumSphUpdate = 0; n < N_gas; n++)
{
SphP[n].Left = SphP[n].Right = 0;
#ifdef AVOIDNUMNGBPROBLEM
SphP[n].OldNumNgb = -1;
#endif
#ifdef ART_CONDUCTIVITY
SphP[n].EnergyIntPred = GAMMA_MINUS1*SphP[n].Pressure/SphP[n].Density ;
#endif
#ifdef SFR
if((P[n].Ti_endstep == All.Ti_Current) && (P[n].Type == 0))
#else
if(P[n].Ti_endstep == All.Ti_Current)
#endif
NumSphUpdate++;
}
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
/* we will repeat the whole thing for those particles where we didn't
* find enough neighbours
*/
do
{
i = 0; /* beginn with this index */
ntotleft = ntot; /* particles left for all tasks together */
while(ntotleft > 0)
{
for(j = 0; j < NTask; j++)
nsend_local[j] = 0;
/* do local particles and prepare export list */
tstart = second();
for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeDensity - NTask; i++)
#ifdef SFR
if((P[i].Ti_endstep == All.Ti_Current) && (P[i].Type == 0))
#else
if(P[i].Ti_endstep == All.Ti_Current)
#endif
{
ndone++;
for(j = 0; j < NTask; j++)
Exportflag[j] = 0;
density_evaluate(i, 0);
for(j = 0; j < NTask; j++)
{
if(Exportflag[j])
{
DensDataIn[nexport].Pos[0] = P[i].Pos[0];
DensDataIn[nexport].Pos[1] = P[i].Pos[1];
DensDataIn[nexport].Pos[2] = P[i].Pos[2];
DensDataIn[nexport].Vel[0] = SphP[i].VelPred[0];
DensDataIn[nexport].Vel[1] = SphP[i].VelPred[1];
DensDataIn[nexport].Vel[2] = SphP[i].VelPred[2];
DensDataIn[nexport].Hsml = SphP[i].Hsml;
#ifdef MULTIPHASE
DensDataIn[nexport].Phase = SphP[i].Phase;
#endif
DensDataIn[nexport].Index = i;
DensDataIn[nexport].Task = j;
#ifdef ART_CONDUCTIVITY
DensDataIn[nexport].EnergyIntPred = SphP[i].EnergyIntPred ;
#endif
nexport++;
nsend_local[j]++;
}
}
}
tend = second();
timecomp += timediff(tstart, tend);
qsort(DensDataIn, nexport, sizeof(struct densdata_in), dens_compare_key);
for(j = 1, noffset[0] = 0; j < NTask; j++)
noffset[j] = noffset[j - 1] + nsend_local[j - 1];
tstart = second();
MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* now do the particles that need to be exported */
for(level = 1; level < (1 << PTask); level++)
{
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeDensity)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* get the particles */
MPI_Sendrecv(&DensDataIn[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct densdata_in), MPI_BYTE,
recvTask, TAG_DENS_A,
&DensDataGet[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_in),
MPI_BYTE, recvTask, TAG_DENS_A, MPI_COMM_WORLD, &status);
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
tstart = second();
for(j = 0; j < nbuffer[ThisTask]; j++)
density_evaluate(j, 1);
tend = second();
timecomp += timediff(tstart, tend);
/* do a block to explicitly measure imbalance */
tstart = second();
MPI_Barrier(MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* get the result */
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeDensity)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* send the results */
MPI_Sendrecv(&DensDataResult[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct densdata_out),
MPI_BYTE, recvTask, TAG_DENS_B,
&DensDataPartialResult[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct densdata_out),
MPI_BYTE, recvTask, TAG_DENS_B, MPI_COMM_WORLD, &status);
/* add the result to the particles */
for(j = 0; j < nsend_local[recvTask]; j++)
{
source = j + noffset[recvTask];
place = DensDataIn[source].Index;
SphP[place].NumNgb += DensDataPartialResult[source].Ngb;
SphP[place].Density += DensDataPartialResult[source].Rho;
SphP[place].DivVel += DensDataPartialResult[source].Div;
SphP[place].DhsmlDensityFactor += DensDataPartialResult[source].DhsmlDensity;
SphP[place].Rot[0] += DensDataPartialResult[source].Rot[0];
SphP[place].Rot[1] += DensDataPartialResult[source].Rot[1];
SphP[place].Rot[2] += DensDataPartialResult[source].Rot[2];
#ifdef ART_CONDUCTIVITY
SphP[place].GradEnergyInt[0] += DensDataPartialResult[source].GradEnergyInt[0];
SphP[place].GradEnergyInt[1] += DensDataPartialResult[source].GradEnergyInt[1];
SphP[place].GradEnergyInt[2] += DensDataPartialResult[source].GradEnergyInt[2];
#endif
}
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
level = ngrp - 1;
}
MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD);
for(j = 0; j < NTask; j++)
ntotleft -= ndonelist[j];
}
/* do final operations on results */
tstart = second();
for(i = 0, npleft = 0; i < N_gas; i++)
{
#ifdef SFR
if((P[i].Ti_endstep == All.Ti_Current) && (P[i].Type == 0))
#else
if(P[i].Ti_endstep == All.Ti_Current)
#endif
{
{
SphP[i].DhsmlDensityFactor =
1 / (1 + SphP[i].Hsml * SphP[i].DhsmlDensityFactor / (NUMDIMS * SphP[i].Density));
SphP[i].CurlVel = sqrt(SphP[i].Rot[0] * SphP[i].Rot[0] +
SphP[i].Rot[1] * SphP[i].Rot[1] +
SphP[i].Rot[2] * SphP[i].Rot[2]) / SphP[i].Density;
SphP[i].DivVel /= SphP[i].Density;
dt_entr = (All.Ti_Current - (P[i].Ti_begstep + P[i].Ti_endstep) / 2) * All.Timebase_interval;
SphP[i].Pressure =
(SphP[i].Entropy + SphP[i].DtEntropy * dt_entr) * pow(SphP[i].Density, GAMMA);
#ifdef ART_CONDUCTIVITY
SphP[i].GradEnergyInt[0] /= SphP[i].Density;
SphP[i].GradEnergyInt[1] /= SphP[i].Density;
SphP[i].GradEnergyInt[2] /= SphP[i].Density;
#endif
}
/* now check whether we had enough neighbours */
if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation) ||
(SphP[i].NumNgb > (All.DesNumNgb + All.MaxNumNgbDeviation)
&& SphP[i].Hsml > (1.01 * All.MinGasHsml)))
{
#ifdef AVOIDNUMNGBPROBLEM
// if((SphP[i].NumNgb>SphP[i].OldNumNgb-All.MaxNumNgbDeviation/10000.)
// &&(SphP[i].NumNgb<SphP[i].OldNumNgb+All.MaxNumNgbDeviation/10000.))
if(SphP[i].NumNgb==SphP[i].OldNumNgb)
{
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
printf("ID=%d NumNgb=%g OldNumNgb=%g\n",P[i].ID,SphP[i].NumNgb,SphP[i].OldNumNgb);
continue;
}
SphP[i].OldNumNgb = SphP[i].NumNgb;
#endif
/* need to redo this particle */
npleft++;
if(SphP[i].Left > 0 && SphP[i].Right > 0)
if((SphP[i].Right - SphP[i].Left) < 1.0e-3 * SphP[i].Left)
{
/* this one should be ok */
npleft--;
P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */
continue;
}
if(SphP[i].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation))
SphP[i].Left = dmax(SphP[i].Hsml, SphP[i].Left);
else
{
if(SphP[i].Right != 0)
{
if(SphP[i].Hsml < SphP[i].Right)
SphP[i].Right = SphP[i].Hsml;
}
else
SphP[i].Right = SphP[i].Hsml;
}
if(iter >= MAXITER - 10)
{
printf
("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n",
i, ThisTask, (int) P[i].ID, SphP[i].Hsml, SphP[i].Left, SphP[i].Right,
(float) SphP[i].NumNgb, SphP[i].Right - SphP[i].Left, P[i].Pos[0], P[i].Pos[1],
P[i].Pos[2]);
fflush(stdout);
}
if(SphP[i].Right > 0 && SphP[i].Left > 0)
SphP[i].Hsml = pow(0.5 * (pow(SphP[i].Left, 3) + pow(SphP[i].Right, 3)), 1.0 / 3);
else
{
if(SphP[i].Right == 0 && SphP[i].Left == 0)
{
printf
("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n",
i, ThisTask, (int) P[i].ID, SphP[i].Hsml, SphP[i].Left, SphP[i].Right,
(float) SphP[i].NumNgb, SphP[i].Right - SphP[i].Left, P[i].Pos[0], P[i].Pos[1],
P[i].Pos[2]);
fflush(stdout);
endrun(8188); /* can't occur */
}
if(SphP[i].Right == 0 && SphP[i].Left > 0)
{
if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb)
{
SphP[i].Hsml *=
1 - (SphP[i].NumNgb -
All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor;
}
else
SphP[i].Hsml *= 1.26;
}
if(SphP[i].Right > 0 && SphP[i].Left == 0)
{
if(P[i].Type == 0 && fabs(SphP[i].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb)
{
SphP[i].Hsml *=
1 - (SphP[i].NumNgb -
All.DesNumNgb) / (NUMDIMS * SphP[i].NumNgb) * SphP[i].DhsmlDensityFactor;
}
else
SphP[i].Hsml /= 1.26;
}
}
if(SphP[i].Hsml < All.MinGasHsml)
SphP[i].Hsml = All.MinGasHsml;
}
else
P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */
}
}
tend = second();
timecomp += timediff(tstart, tend);
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&npleft, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
if(ntot > 0)
{
if(iter == 0)
tstart_ngb = second();
iter++;
if(iter > 0 && ThisTask == 0)
{
printf("ngb iteration %d: need to repeat for %d%09d particles.\n", iter,
(int) (ntot / 1000000000), (int) (ntot % 1000000000));
fflush(stdout);
}
if(iter > MAXITER)
{
printf("failed to converge in neighbour iteration in density()\n");
fflush(stdout);
endrun(1155);
}
}
else
tend_ngb = second();
}
while(ntot > 0);
/* mark as active again */
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep < 0)
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
free(ndonelist);
free(nsend);
free(nsend_local);
free(nbuffer);
free(noffset);
/* collect some timing information */
if(iter > 0)
timengb = timediff(tstart_ngb, tend_ngb);
else
timengb = 0;
MPI_Reduce(&timengb, &sumtimengb, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
+#ifdef DETAILED_CPU_OUTPUT_IN_DENSITY
+ numlist = malloc(sizeof(int) * NTask);
+ timengblist = malloc(sizeof(double) * NTask);
+ timecomplist = malloc(sizeof(double) * NTask);
+ timecommsummlist = malloc(sizeof(double) * NTask);
+ timeimbalancelist = malloc(sizeof(double) * NTask);
+
+ MPI_Gather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timengb, 1, MPI_DOUBLE, timengblist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecomp, 1, MPI_DOUBLE, timecomplist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecommsumm, 1, MPI_DOUBLE, timecommsummlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timeimbalance, 1, MPI_DOUBLE, timeimbalancelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+
+ if(ThisTask == 0)
+ {
+ fprintf(FdTimings, "\n density (mode=%d)\n\n",mode);
+
+ fprintf(FdTimings, "Nupdate ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12d ",numlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timengb ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timengblist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecomp ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecomplist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecommsumm ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecommsummlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timeimbalance ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timeimbalancelist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "\n");
+ }
+
+ free(timeimbalancelist);
+ free(timecommsummlist);
+ free(timecomplist);
+ free(numlist);
+
+#endif
#ifdef DETAILED_CPU
if (mode==1)
{
t1 = second();
All.CPU_StarFormation += timediff(t0, t1);
}
else
if(ThisTask == 0)
{
All.CPU_HydCompWalk += sumt / NTask;
All.CPU_HydCommSumm += sumcomm / NTask;
All.CPU_HydImbalance += sumimbalance / NTask;
All.CPU_EnsureNgb += sumtimengb / NTask;
}
#else
if(ThisTask == 0)
{
All.CPU_HydCompWalk += sumt / NTask;
All.CPU_HydCommSumm += sumcomm / NTask;
All.CPU_HydImbalance += sumimbalance / NTask;
All.CPU_EnsureNgb += sumtimengb / NTask;
}
#endif
}
/*! This function represents the core of the SPH density computation. The
* target particle may either be local, or reside in the communication
* buffer.
*/
void density_evaluate(int target, int mode)
{
int j, n, startnode, numngb, numngb_inbox;
double h, h2, fac, hinv, hinv3, hinv4;
double rho, divv, wk, dwk;
double dx, dy, dz, r, r2, u, mass_j;
double dvx, dvy, dvz, rotv[3];
double weighted_numngb, dhsmlrho;
FLOAT *pos, *vel;
int phase=0;
#ifdef ART_CONDUCTIVITY
double gradux, graduy, graduz;
double energyintpred;
#endif
if(mode == 0)
{
pos = P[target].Pos;
vel = SphP[target].VelPred;
h = SphP[target].Hsml;
#ifdef MULTIPHASE
phase = SphP[target].Phase;
#endif
#ifdef ART_CONDUCTIVITY
energyintpred = SphP[target].EnergyIntPred;
#endif
}
else
{
pos = DensDataGet[target].Pos;
vel = DensDataGet[target].Vel;
h = DensDataGet[target].Hsml;
#ifdef MULTIPHASE
phase = DensDataGet[target].Phase;
#endif
#ifdef ART_CONDUCTIVITY
energyintpred = DensDataGet[target].EnergyIntPred;
#endif
}
h2 = h * h;
hinv = 1.0 / h;
#ifndef TWODIMS
hinv3 = hinv * hinv * hinv;
#else
hinv3 = hinv * hinv / boxSize_Z;
#endif
hinv4 = hinv3 * hinv;
rho = divv = rotv[0] = rotv[1] = rotv[2] = 0;
weighted_numngb = 0;
dhsmlrho = 0;
#ifdef ART_CONDUCTIVITY
gradux=graduy=graduz=0;
#endif
startnode = All.MaxPart;
numngb = 0;
do
{
numngb_inbox = ngb_treefind_variable(&pos[0], h, phase, &startnode);
for(n = 0; n < numngb_inbox; n++)
{
j = Ngblist[n];
dx = pos[0] - P[j].Pos[0];
dy = pos[1] - P[j].Pos[1];
dz = pos[2] - P[j].Pos[2];
#ifdef PERIODIC /* now find the closest image in the given box size */
if(dx > boxHalf_X)
dx -= boxSize_X;
if(dx < -boxHalf_X)
dx += boxSize_X;
if(dy > boxHalf_Y)
dy -= boxSize_Y;
if(dy < -boxHalf_Y)
dy += boxSize_Y;
if(dz > boxHalf_Z)
dz -= boxSize_Z;
if(dz < -boxHalf_Z)
dz += boxSize_Z;
#endif
r2 = dx * dx + dy * dy + dz * dz;
if(r2 < h2)
{
numngb++;
r = sqrt(r2);
u = r * hinv;
if(u < 0.5)
{
wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u);
dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4);
}
else
{
wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u);
dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u);
}
mass_j = P[j].Mass;
rho += mass_j * wk;
weighted_numngb += NORM_COEFF * wk / hinv3;
dhsmlrho += -mass_j * (NUMDIMS * hinv * wk + u * dwk);
if(r > 0)
{
fac = mass_j * dwk / r;
dvx = vel[0] - SphP[j].VelPred[0];
dvy = vel[1] - SphP[j].VelPred[1];
dvz = vel[2] - SphP[j].VelPred[2];
divv -= fac * (dx * dvx + dy * dvy + dz * dvz);
rotv[0] += fac * (dz * dvy - dy * dvz);
rotv[1] += fac * (dx * dvz - dz * dvx);
rotv[2] += fac * (dy * dvx - dx * dvy);
#ifdef ART_CONDUCTIVITY
if (All.NumCurrentTiStep==0)
fac = 0;
gradux += fac * (energyintpred-SphP[j].EnergyIntPred)*dx;
graduy += fac * (energyintpred-SphP[j].EnergyIntPred)*dy;
graduz += fac * (energyintpred-SphP[j].EnergyIntPred)*dz;
#endif
}
}
}
}
while(startnode >= 0);
if(mode == 0)
{
SphP[target].NumNgb = weighted_numngb;
SphP[target].Density = rho;
SphP[target].DivVel = divv;
SphP[target].DhsmlDensityFactor = dhsmlrho;
SphP[target].Rot[0] = rotv[0];
SphP[target].Rot[1] = rotv[1];
SphP[target].Rot[2] = rotv[2];
#ifdef ART_CONDUCTIVITY
SphP[target].GradEnergyInt[0] = gradux;
SphP[target].GradEnergyInt[1] = graduy;
SphP[target].GradEnergyInt[2] = graduz;
#endif
}
else
{
DensDataResult[target].Rho = rho;
DensDataResult[target].Div = divv;
DensDataResult[target].Ngb = weighted_numngb;
DensDataResult[target].DhsmlDensity = dhsmlrho;
DensDataResult[target].Rot[0] = rotv[0];
DensDataResult[target].Rot[1] = rotv[1];
DensDataResult[target].Rot[2] = rotv[2];
#ifdef ART_CONDUCTIVITY
DensDataResult[target].GradEnergyInt[0] = gradux;
DensDataResult[target].GradEnergyInt[1] = graduy;
DensDataResult[target].GradEnergyInt[2] = graduz;
#endif
}
}
/*! This routine is a comparison kernel used in a sort routine to group
* particles that are exported to the same processor.
*/
int dens_compare_key(const void *a, const void *b)
{
if(((struct densdata_in *) a)->Task < (((struct densdata_in *) b)->Task))
return -1;
if(((struct densdata_in *) a)->Task > (((struct densdata_in *) b)->Task))
return +1;
return 0;
}
diff --git a/gravtree.c b/gravtree.c
index 97c9124..5f59599 100644
--- a/gravtree.c
+++ b/gravtree.c
@@ -1,662 +1,678 @@
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <mpi.h>
#include "allvars.h"
#include "proto.h"
/*! \file gravtree.c
* \brief main driver routines for gravitational (short-range) force computation
*
* This file contains the code for the gravitational force computation by
* means of the tree algorithm. To this end, a tree force is computed for
* all active local particles, and particles are exported to other
* processors if needed, where they can receive additional force
* contributions. If the TreePM algorithm is enabled, the force computed
* will only be the short-range part.
*/
/*! This function computes the gravitational forces for all active
* particles. If needed, a new tree is constructed, otherwise the
* dynamically updated tree is used. Particles are only exported to other
* processors when really needed, thereby allowing a good use of the
* communication buffer.
*/
void gravity_tree(void)
{
long long ntot;
int numnodes, nexportsum = 0;
int i, j, iter = 0;
int *numnodeslist, maxnumnodes, nexport, *numlist, *nrecv, *ndonelist;
double tstart, tend, timetree = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance;
double ewaldcount;
double costtotal, ewaldtot, *costtreelist, *ewaldlist;
double maxt, sumt, *timetreelist, *timecommlist;
double fac, plb, plb_max, sumcomm;
#ifdef DETAILED_CPU_GRAVITY
double timetree1 = 0, timecommsumm1 = 0, timeimbalance1 = 0;
double timetree2 = 0, timecommsumm2 = 0, timeimbalance2 = 0;
double sumt1,sumt2;
double sumcomm1,sumcomm2;
double sumimbalance1,sumimbalance2;
#endif
-#ifdef DETAILED_OUTPUT_IN_GRAVTREE
+#ifdef DETAILED_CPU_OUTPUT_IN_GRAVTREE
double *timetreelist1, *timecommlist1, *timeimbalancelist1;
double *timetreelist2, *timecommlist2, *timeimbalancelist2;
double *timeimbalancelist;
int *numpartlist;
#endif
#ifndef NOGRAVITY
int *noffset, *nbuffer, *nsend, *nsend_local;
long long ntotleft;
int ndone, maxfill, ngrp;
int k, place;
int level, sendTask, recvTask;
double ax, ay, az;
MPI_Status status;
#endif
/* set new softening lengths */
if(All.ComovingIntegrationOn)
set_softenings();
/* contruct tree if needed */
tstart = second();
if(TreeReconstructFlag)
{
if(ThisTask == 0)
printf("Tree construction.\n");
force_treebuild(NumPart);
TreeReconstructFlag = 0;
if(ThisTask == 0)
printf("Tree construction done.\n");
}
tend = second();
All.CPU_TreeConstruction += timediff(tstart, tend);
costtotal = ewaldcount = 0;
/* Note: 'NumForceUpdate' has already been determined in find_next_sync_point_and_drift() */
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&NumForceUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
#ifndef NOGRAVITY
if(ThisTask == 0)
printf("Begin tree force.\n");
#ifdef SELECTIVE_NO_GRAVITY
for(i = 0; i < NumPart; i++)
if(((1 << P[i].Type) & (SELECTIVE_NO_GRAVITY)))
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
#endif
noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */
nbuffer = malloc(sizeof(int) * NTask);
nsend_local = malloc(sizeof(int) * NTask);
nsend = malloc(sizeof(int) * NTask * NTask);
ndonelist = malloc(sizeof(int) * NTask);
i = 0; /* beginn with this index */
ntotleft = ntot; /* particles left for all tasks together */
while(ntotleft > 0)
{
iter++;
for(j = 0; j < NTask; j++)
nsend_local[j] = 0;
/* do local particles and prepare export list */
tstart = second();
for(nexport = 0, ndone = 0; i < NumPart && nexport < All.BunchSizeForce - NTask; i++)
if(P[i].Ti_endstep == All.Ti_Current)
{
ndone++;
for(j = 0; j < NTask; j++)
Exportflag[j] = 0;
#ifndef PMGRID
costtotal += force_treeevaluate(i, 0, &ewaldcount);
#else
costtotal += force_treeevaluate_shortrange(i, 0);
#endif
for(j = 0; j < NTask; j++)
{
if(Exportflag[j])
{
for(k = 0; k < 3; k++)
GravDataGet[nexport].u.Pos[k] = P[i].Pos[k];
#if defined(UNEQUALSOFTENINGS) || defined(STELLAR_FLUX)
GravDataGet[nexport].Type = P[i].Type;
#ifdef ADAPTIVE_GRAVSOFT_FORGAS
if(P[i].Type == 0)
GravDataGet[nexport].Soft = SphP[i].Hsml;
#endif
#endif
GravDataGet[nexport].w.OldAcc = P[i].OldAcc;
GravDataIndexTable[nexport].Task = j;
GravDataIndexTable[nexport].Index = i;
GravDataIndexTable[nexport].SortIndex = nexport;
nexport++;
nexportsum++;
nsend_local[j]++;
}
}
}
tend = second();
timetree += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timetree1 += timediff(tstart, tend);
#endif
qsort(GravDataIndexTable, nexport, sizeof(struct gravdata_index), grav_tree_compare_key);
for(j = 0; j < nexport; j++)
GravDataIn[j] = GravDataGet[GravDataIndexTable[j].SortIndex];
for(j = 1, noffset[0] = 0; j < NTask; j++)
noffset[j] = noffset[j - 1] + nsend_local[j - 1];
tstart = second();
MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timeimbalance1 += timediff(tstart, tend);
#endif
/* now do the particles that need to be exported */
for(level = 1; level < (1 << PTask); level++)
{
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeForce)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* get the particles */
MPI_Sendrecv(&GravDataIn[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct gravdata_in), MPI_BYTE,
recvTask, TAG_GRAV_A,
&GravDataGet[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct gravdata_in), MPI_BYTE,
recvTask, TAG_GRAV_A, MPI_COMM_WORLD, &status);
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timecommsumm1 += timediff(tstart, tend);
#endif
tstart = second();
for(j = 0; j < nbuffer[ThisTask]; j++)
{
#ifndef PMGRID
costtotal += force_treeevaluate(j, 1, &ewaldcount);
#else
costtotal += force_treeevaluate_shortrange(j, 1);
#endif
}
tend = second();
timetree += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timetree2 += timediff(tstart, tend);
#endif
tstart = second();
MPI_Barrier(MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timeimbalance2 += timediff(tstart, tend);
#endif
/* get the result */
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeForce)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* send the results */
MPI_Sendrecv(&GravDataResult[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct gravdata_in),
MPI_BYTE, recvTask, TAG_GRAV_B,
&GravDataOut[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct gravdata_in),
MPI_BYTE, recvTask, TAG_GRAV_B, MPI_COMM_WORLD, &status);
/* add the result to the particles */
for(j = 0; j < nsend_local[recvTask]; j++)
{
place = GravDataIndexTable[noffset[recvTask] + j].Index;
for(k = 0; k < 3; k++)
P[place].GravAccel[k] += GravDataOut[j + noffset[recvTask]].u.Acc[k];
P[place].GravCost += GravDataOut[j + noffset[recvTask]].w.Ninteractions;
#ifdef STELLAR_FLUX
if (P[place].Type==0) /* only for gas */
SphP[place].EnergyFlux += GravDataOut[j + noffset[recvTask]].EnergyFlux;
#endif
}
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
#ifdef DETAILED_CPU_GRAVITY
timecommsumm2 += timediff(tstart, tend);
#endif
level = ngrp - 1;
}
MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD);
for(j = 0; j < NTask; j++)
ntotleft -= ndonelist[j];
}
free(ndonelist);
free(nsend);
free(nsend_local);
free(nbuffer);
free(noffset);
/* now add things for comoving integration */
/* yr: with vacuum boundary conditions */
#ifndef PERIODIC
#ifndef PMGRID
if(All.ComovingIntegrationOn)
{
fac = 0.5 * All.Hubble * All.Hubble * All.Omega0 / All.G;
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep == All.Ti_Current)
for(j = 0; j < 3; j++)
P[i].GravAccel[j] += fac * P[i].Pos[j];
}
#endif
#endif
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep == All.Ti_Current)
{
#ifdef PMGRID
ax = P[i].GravAccel[0] + P[i].GravPM[0] / All.G;
ay = P[i].GravAccel[1] + P[i].GravPM[1] / All.G;
az = P[i].GravAccel[2] + P[i].GravPM[2] / All.G;
#else
ax = P[i].GravAccel[0];
ay = P[i].GravAccel[1];
az = P[i].GravAccel[2];
#endif
P[i].OldAcc = sqrt(ax * ax + ay * ay + az * az);
}
if(All.TypeOfOpeningCriterion == 1)
All.ErrTolTheta = 0; /* This will switch to the relative opening criterion for the following force computations */
/* muliply by G */
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep == All.Ti_Current)
for(j = 0; j < 3; j++)
P[i].GravAccel[j] *= All.G;
/* Finally, the following factor allows a computation of a cosmological simulation
with vacuum energy in physical coordinates */
#ifndef PERIODIC
#ifndef PMGRID
if(All.ComovingIntegrationOn == 0)
{
fac = All.OmegaLambda * All.Hubble * All.Hubble;
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep == All.Ti_Current)
for(j = 0; j < 3; j++)
P[i].GravAccel[j] += fac * P[i].Pos[j];
}
#endif
#endif
#ifdef SELECTIVE_NO_GRAVITY
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep < 0)
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
#endif
if(ThisTask == 0)
printf("tree is done.\n");
#else /* gravity is switched off */
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep == All.Ti_Current)
for(j = 0; j < 3; j++)
P[i].GravAccel[j] = 0;
#endif
#ifdef OUTERPOTENTIAL
/* Add outer forces from an outer potential */
outer_forces();
#endif
/* Now the force computation is finished */
/* gather some diagnostic information */
timetreelist = malloc(sizeof(double) * NTask);
timecommlist = malloc(sizeof(double) * NTask);
costtreelist = malloc(sizeof(double) * NTask);
numnodeslist = malloc(sizeof(int) * NTask);
ewaldlist = malloc(sizeof(double) * NTask);
nrecv = malloc(sizeof(int) * NTask);
-#ifdef DETAILED_OUTPUT_IN_GRAVTREE
+#ifdef DETAILED_CPU_OUTPUT_IN_GRAVTREE
timeimbalancelist = malloc(sizeof(double) * NTask);
numpartlist = malloc(sizeof(int) * NTask);
timetreelist1 = malloc(sizeof(double) * NTask);
timecommlist1 = malloc(sizeof(double) * NTask);
timeimbalancelist1 = malloc(sizeof(double) * NTask);
timetreelist2 = malloc(sizeof(double) * NTask);
timecommlist2 = malloc(sizeof(double) * NTask);
timeimbalancelist2 = malloc(sizeof(double) * NTask);
#endif
numnodes = Numnodestree;
MPI_Gather(&costtotal, 1, MPI_DOUBLE, costtreelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&numnodes, 1, MPI_INT, numnodeslist, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Gather(&timetree, 1, MPI_DOUBLE, timetreelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timecommsumm, 1, MPI_DOUBLE, timecommlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&NumPart, 1, MPI_INT, nrecv, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Gather(&ewaldcount, 1, MPI_DOUBLE, ewaldlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Reduce(&nexportsum, &nexport, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
#ifdef DETAILED_CPU_GRAVITY
MPI_Reduce(&timetree1, &sumt1, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timetree2, &sumt2, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance1, &sumimbalance1, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance2, &sumimbalance2, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm1, &sumcomm1, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm2, &sumcomm2, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
#endif
-#ifdef DETAILED_OUTPUT_IN_GRAVTREE
+#ifdef DETAILED_CPU_OUTPUT_IN_GRAVTREE
MPI_Gather(&timeimbalance, 1, MPI_DOUBLE, timeimbalancelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
- MPI_Gather(&NumPart, 1, MPI_INT, numpartlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Gather(&NumForceUpdate, 1, MPI_INT, numpartlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Gather(&timetree1, 1, MPI_DOUBLE, timetreelist1, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timetree2, 1, MPI_DOUBLE, timetreelist2, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timecommsumm1, 1, MPI_DOUBLE, timecommlist1, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timecommsumm2, 1, MPI_DOUBLE, timecommlist2, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timeimbalance1, 1, MPI_DOUBLE, timeimbalancelist1, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
MPI_Gather(&timeimbalance2, 1, MPI_DOUBLE, timeimbalancelist2, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
#endif
#ifdef SPLIT_DOMAIN_USING_TIME
CPU_Gravity = timetree1;
#endif
if(ThisTask == 0)
{
All.TotNumOfForces += ntot;
fprintf(FdTimings, "Step= %d t= %g dt= %g \n", All.NumCurrentTiStep, All.Time, All.TimeStep);
fprintf(FdTimings, "Nf= %d%09d total-Nf= %d%09d ex-frac= %g iter= %d\n",
(int) (ntot / 1000000000), (int) (ntot % 1000000000),
(int) (All.TotNumOfForces / 1000000000), (int) (All.TotNumOfForces % 1000000000),
nexport / ((double) ntot), iter);
/* note: on Linux, the 8-byte integer could be printed with the format identifier "%qd", but doesn't work on AIX */
fac = NTask / ((double) All.TotNumPart);
for(i = 0, maxt = timetreelist[0], sumt = 0, plb_max = 0,
maxnumnodes = 0, costtotal = 0, sumcomm = 0, ewaldtot = 0; i < NTask; i++)
{
costtotal += costtreelist[i];
sumcomm += timecommlist[i];
if(maxt < timetreelist[i])
maxt = timetreelist[i];
sumt += timetreelist[i];
plb = nrecv[i] * fac;
if(plb > plb_max)
plb_max = plb;
if(numnodeslist[i] > maxnumnodes)
maxnumnodes = numnodeslist[i];
ewaldtot += ewaldlist[i];
}
fprintf(FdTimings, "work-load balance: %g max=%g avg=%g PE0=%g\n",
maxt / (sumt / NTask), maxt, sumt / NTask, timetreelist[0]);
fprintf(FdTimings, "particle-load balance: %g\n", plb_max);
fprintf(FdTimings, "max. nodes: %d, filled: %g\n", maxnumnodes,
maxnumnodes / (All.TreeAllocFactor * All.MaxPart));
fprintf(FdTimings, "part/sec=%g | %g ia/part=%g (%g)\n", ntot / (sumt + 1.0e-20),
ntot / (maxt * NTask), ((double) (costtotal)) / ntot, ((double) ewaldtot) / ntot);
-#ifdef DETAILED_OUTPUT_IN_GRAVTREE
+#ifdef DETAILED_CPU_OUTPUT_IN_GRAVTREE
+ fprintf(FdTimings, "\n gravtree\n\n");
+
+ fprintf(FdTimings, "Nupdate ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12d ",numpartlist[i]); /* nombre de part par proc */
fprintf(FdTimings, "\n");
-
+ fprintf(FdTimings, "costtree ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",costtreelist[i]); /* nombre d'interactions */
fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "timetree ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timetreelist[i]);
fprintf(FdTimings, "\n");
-
+
+ fprintf(FdTimings, "timeimbalance ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timeimbalancelist[i]);
fprintf(FdTimings, "\n");
-
+
+ fprintf(FdTimings, "timecomm ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timecommlist[i]);
fprintf(FdTimings, "\n");
-
+
+ fprintf(FdTimings, "timetree1 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timetreelist1[i]);
- fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timetree2 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timetreelist2[i]);
fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "timeimbalance1 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timeimbalancelist1[i]);
fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timeimbalance2 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timeimbalancelist2[i]);
fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "timecomm1 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timecommlist1[i]);
- fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecomm2 ");
for (i=0;i<NTask;i++)
fprintf(FdTimings, "%12g ",timecommlist2[i]);
fprintf(FdTimings, "\n");
+ fprintf(FdTimings, "\n");
#endif
fprintf(FdTimings, "\n");
fflush(FdTimings);
All.CPU_TreeWalk += sumt / NTask;
All.CPU_Imbalance += sumimbalance / NTask;
All.CPU_CommSum += sumcomm / NTask;
#ifdef DETAILED_CPU_GRAVITY
All.CPU_Gravity_TreeWalk1 += sumt1 / NTask;
All.CPU_Gravity_Imbalance1 += sumimbalance1 / NTask;
All.CPU_Gravity_CommSum1 += sumcomm1 / NTask;
All.CPU_Gravity_TreeWalk2 += sumt2 / NTask;
All.CPU_Gravity_Imbalance2 += sumimbalance2 / NTask;
All.CPU_Gravity_CommSum2 += sumcomm2 / NTask;
#endif
}
#ifdef DETAILED_CPU_GRAVITY
free(timeimbalancelist2);
free(timecommlist2);
free(timetreelist2);
free(timeimbalancelist1);
free(timecommlist1);
free(timetreelist1);
free(numpartlist);
free(timeimbalancelist);
#endif
free(nrecv);
free(ewaldlist);
free(numnodeslist);
free(costtreelist);
free(timecommlist);
free(timetreelist);
}
/*! This function sets the (comoving) softening length of all particle
* types in the table All.SofteningTable[...]. We check that the physical
* softening length is bounded by the Softening-MaxPhys values.
*/
void set_softenings(void)
{
int i;
if(All.ComovingIntegrationOn)
{
if(All.SofteningGas * All.Time > All.SofteningGasMaxPhys)
All.SofteningTable[0] = All.SofteningGasMaxPhys / All.Time;
else
All.SofteningTable[0] = All.SofteningGas;
if(All.SofteningHalo * All.Time > All.SofteningHaloMaxPhys)
All.SofteningTable[1] = All.SofteningHaloMaxPhys / All.Time;
else
All.SofteningTable[1] = All.SofteningHalo;
if(All.SofteningDisk * All.Time > All.SofteningDiskMaxPhys)
All.SofteningTable[2] = All.SofteningDiskMaxPhys / All.Time;
else
All.SofteningTable[2] = All.SofteningDisk;
if(All.SofteningBulge * All.Time > All.SofteningBulgeMaxPhys)
All.SofteningTable[3] = All.SofteningBulgeMaxPhys / All.Time;
else
All.SofteningTable[3] = All.SofteningBulge;
if(All.SofteningStars * All.Time > All.SofteningStarsMaxPhys)
All.SofteningTable[4] = All.SofteningStarsMaxPhys / All.Time;
else
All.SofteningTable[4] = All.SofteningStars;
if(All.SofteningBndry * All.Time > All.SofteningBndryMaxPhys)
All.SofteningTable[5] = All.SofteningBndryMaxPhys / All.Time;
else
All.SofteningTable[5] = All.SofteningBndry;
}
else
{
All.SofteningTable[0] = All.SofteningGas;
All.SofteningTable[1] = All.SofteningHalo;
All.SofteningTable[2] = All.SofteningDisk;
All.SofteningTable[3] = All.SofteningBulge;
All.SofteningTable[4] = All.SofteningStars;
All.SofteningTable[5] = All.SofteningBndry;
}
for(i = 0; i < 6; i++)
All.ForceSoftening[i] = 2.8 * All.SofteningTable[i];
All.MinGasHsml = All.MinGasHsmlFractional * All.ForceSoftening[0];
}
/*! This function is used as a comparison kernel in a sort routine. It is
* used to group particles in the communication buffer that are going to
* be sent to the same CPU.
*/
int grav_tree_compare_key(const void *a, const void *b)
{
if(((struct gravdata_index *) a)->Task < (((struct gravdata_index *) b)->Task))
return -1;
if(((struct gravdata_index *) a)->Task > (((struct gravdata_index *) b)->Task))
return +1;
return 0;
}
diff --git a/hydra.c b/hydra.c
index b1a6ffd..02b2db1 100644
--- a/hydra.c
+++ b/hydra.c
@@ -1,832 +1,887 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include <gsl/gsl_math.h>
#include "allvars.h"
#include "proto.h"
/*! \file hydra.c
* \brief Computation of SPH forces and rate of entropy generation
*
* This file contains the "second SPH loop", where the SPH forces are
* computed, and where the rate of change of entropy due to the shock heating
* (via artificial viscosity) is computed.
*/
static double hubble_a, atime, hubble_a2, fac_mu, fac_vsic_fix, a3inv, fac_egy;
#ifdef FEEDBACK
static double fac_pow;
#endif
#ifdef PERIODIC
static double boxSize, boxHalf;
#ifdef LONG_X
static double boxSize_X, boxHalf_X;
#else
#define boxSize_X boxSize
#define boxHalf_X boxHalf
#endif
#ifdef LONG_Y
static double boxSize_Y, boxHalf_Y;
#else
#define boxSize_Y boxSize
#define boxHalf_Y boxHalf
#endif
#ifdef LONG_Z
static double boxSize_Z, boxHalf_Z;
#else
#define boxSize_Z boxSize
#define boxHalf_Z boxHalf
#endif
#endif
/*! This function is the driver routine for the calculation of hydrodynamical
* force and rate of change of entropy due to shock heating for all active
* particles .
*/
void hydro_force(void)
{
long long ntot, ntotleft;
int i, j, k, n, ngrp, maxfill, source, ndone;
int *nbuffer, *noffset, *nsend_local, *nsend, *numlist, *ndonelist;
int level, sendTask, recvTask, nexport, place;
double soundspeed_i;
double tstart, tend, sumt, sumcomm;
double timecomp = 0, timecommsumm = 0, timeimbalance = 0, sumimbalance;
MPI_Status status;
+
+#ifdef DETAILED_CPU_OUTPUT_IN_HYDRA
+ double *timecomplist;
+ double *timecommsummlist;
+ double *timeimbalancelist;
+#endif
#ifdef PERIODIC
boxSize = All.BoxSize;
boxHalf = 0.5 * All.BoxSize;
#ifdef LONG_X
boxHalf_X = boxHalf * LONG_X;
boxSize_X = boxSize * LONG_X;
#endif
#ifdef LONG_Y
boxHalf_Y = boxHalf * LONG_Y;
boxSize_Y = boxSize * LONG_Y;
#endif
#ifdef LONG_Z
boxHalf_Z = boxHalf * LONG_Z;
boxSize_Z = boxSize * LONG_Z;
#endif
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
double v1m,v2m;
#endif
if(All.ComovingIntegrationOn)
{
/* Factors for comoving integration of hydro */
hubble_a = All.Omega0 / (All.Time * All.Time * All.Time)
+ (1 - All.Omega0 - All.OmegaLambda) / (All.Time * All.Time) + All.OmegaLambda;
hubble_a = All.Hubble * sqrt(hubble_a);
hubble_a2 = All.Time * All.Time * hubble_a;
fac_mu = pow(All.Time, 3 * (GAMMA - 1) / 2) / All.Time;
fac_egy = pow(All.Time, 3 * (GAMMA - 1));
fac_vsic_fix = hubble_a * pow(All.Time, 3 * GAMMA_MINUS1);
a3inv = 1 / (All.Time * All.Time * All.Time);
atime = All.Time;
#ifdef FEEDBACK
fac_pow = fac_egy*atime*atime;
#endif
}
else
{
hubble_a = hubble_a2 = atime = fac_mu = fac_vsic_fix = a3inv = fac_egy = 1.0;
#ifdef FEEDBACK
fac_pow = 1.0;
#endif
}
#ifdef OUTPUT_CONDUCTIVITY
for (i=0;i<N_gas;i++)
SphP[i].OptVar1 = sqrt( pow(SphP[i].GradEnergyInt[0],2)+pow(SphP[i].GradEnergyInt[1],2)+pow(SphP[i].GradEnergyInt[2],2))*SphP[i].Hsml/(SphP[i].Pressure/(SphP[i].Density*GAMMA_MINUS1)) ;
#endif
/* `NumSphUpdate' gives the number of particles on this processor that want a force update */
for(n = 0, NumSphUpdate = 0; n < N_gas; n++)
{
#ifdef SFR
if((P[n].Ti_endstep == All.Ti_Current) && (P[n].Type == 0))
#else
if(P[n].Ti_endstep == All.Ti_Current)
#endif
#ifdef MULTIPHASE
if(SphP[n].Phase == GAS_SPH)
#endif
NumSphUpdate++;
}
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */
nbuffer = malloc(sizeof(int) * NTask);
nsend_local = malloc(sizeof(int) * NTask);
nsend = malloc(sizeof(int) * NTask * NTask);
ndonelist = malloc(sizeof(int) * NTask);
+
i = 0; /* first particle for this task */
ntotleft = ntot; /* particles left for all tasks together */
while(ntotleft > 0)
{
for(j = 0; j < NTask; j++)
nsend_local[j] = 0;
/* do local particles and prepare export list */
tstart = second();
for(nexport = 0, ndone = 0; i < N_gas && nexport < All.BunchSizeHydro - NTask; i++)
#ifdef SFR
if((P[i].Ti_endstep == All.Ti_Current) && (P[i].Type == 0))
#else
if(P[i].Ti_endstep == All.Ti_Current)
#endif
{
#ifdef MULTIPHASE
if(SphP[i].Phase == GAS_SPH)
{
#endif
ndone++;
for(j = 0; j < NTask; j++)
Exportflag[j] = 0;
hydro_evaluate(i, 0);
for(j = 0; j < NTask; j++)
{
if(Exportflag[j])
{
for(k = 0; k < 3; k++)
{
HydroDataIn[nexport].Pos[k] = P[i].Pos[k];
HydroDataIn[nexport].Vel[k] = SphP[i].VelPred[k];
}
HydroDataIn[nexport].Hsml = SphP[i].Hsml;
#ifdef FEEDBACK
HydroDataIn[nexport].EnergySN = SphP[i].EnergySN;
#endif
HydroDataIn[nexport].Mass = P[i].Mass;
HydroDataIn[nexport].DhsmlDensityFactor = SphP[i].DhsmlDensityFactor;
HydroDataIn[nexport].Density = SphP[i].Density;
HydroDataIn[nexport].Pressure = SphP[i].Pressure;
HydroDataIn[nexport].Timestep = P[i].Ti_endstep - P[i].Ti_begstep;
#ifdef WITH_ID_IN_HYDRA
HydroDataIn[nexport].ID = P[i].ID;
#endif
#ifdef ART_CONDUCTIVITY
HydroDataIn[nexport].NormGradEnergyInt = sqrt( pow(SphP[i].GradEnergyInt[0],2)+pow(SphP[i].GradEnergyInt[1],2)+pow(SphP[i].GradEnergyInt[2],2));
#endif
/* calculation of F1 */
soundspeed_i = sqrt(GAMMA * SphP[i].Pressure / SphP[i].Density);
HydroDataIn[nexport].F1 = fabs(SphP[i].DivVel) /
(fabs(SphP[i].DivVel) + SphP[i].CurlVel +
0.0001 * soundspeed_i / SphP[i].Hsml / fac_mu);
HydroDataIn[nexport].Index = i;
HydroDataIn[nexport].Task = j;
nexport++;
nsend_local[j]++;
}
}
#ifdef MULTIPHASE
}
#endif
}
tend = second();
timecomp += timediff(tstart, tend);
qsort(HydroDataIn, nexport, sizeof(struct hydrodata_in), hydro_compare_key);
for(j = 1, noffset[0] = 0; j < NTask; j++)
noffset[j] = noffset[j - 1] + nsend_local[j - 1];
tstart = second();
MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* now do the particles that need to be exported */
for(level = 1; level < (1 << PTask); level++)
{
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeHydro)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* get the particles */
MPI_Sendrecv(&HydroDataIn[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct hydrodata_in), MPI_BYTE,
recvTask, TAG_HYDRO_A,
&HydroDataGet[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct hydrodata_in), MPI_BYTE,
recvTask, TAG_HYDRO_A, MPI_COMM_WORLD, &status);
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
/* now do the imported particles */
tstart = second();
for(j = 0; j < nbuffer[ThisTask]; j++)
hydro_evaluate(j, 1);
tend = second();
timecomp += timediff(tstart, tend);
/* do a block to measure imbalance */
tstart = second();
MPI_Barrier(MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* get the result */
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeHydro)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* send the results */
MPI_Sendrecv(&HydroDataResult[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct hydrodata_out),
MPI_BYTE, recvTask, TAG_HYDRO_B,
&HydroDataPartialResult[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct hydrodata_out),
MPI_BYTE, recvTask, TAG_HYDRO_B, MPI_COMM_WORLD, &status);
/* add the result to the particles */
for(j = 0; j < nsend_local[recvTask]; j++)
{
source = j + noffset[recvTask];
place = HydroDataIn[source].Index;
for(k = 0; k < 3; k++)
SphP[place].HydroAccel[k] += HydroDataPartialResult[source].Acc[k];
SphP[place].DtEntropy += HydroDataPartialResult[source].DtEntropy;
#ifdef FEEDBACK
SphP[place].DtEgySpecFeedback += HydroDataPartialResult[source].DtEgySpecFeedback;
#endif
if(SphP[place].MaxSignalVel < HydroDataPartialResult[source].MaxSignalVel)
SphP[place].MaxSignalVel = HydroDataPartialResult[source].MaxSignalVel;
#ifdef COMPUTE_VELOCITY_DISPERSION
for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
SphP[place].VelocityDispersion[k] += HydroDataPartialResult[source].VelocityDispersion[k];
#endif
#ifdef OUTPUT_CONDUCTIVITY
SphP[place].OptVar2 += HydroDataPartialResult[source].OptVar2;
#endif
}
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
level = ngrp - 1;
}
MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD);
for(j = 0; j < NTask; j++)
ntotleft -= ndonelist[j];
}
free(ndonelist);
free(nsend);
free(nsend_local);
free(nbuffer);
free(noffset);
/* do final operations on results */
tstart = second();
for(i = 0; i < N_gas; i++)
#ifdef SFR
if((P[i].Ti_endstep == All.Ti_Current) && (P[i].Type == 0))
#else
if(P[i].Ti_endstep == All.Ti_Current)
#endif
{
SphP[i].DtEntropy *= GAMMA_MINUS1 / (hubble_a2 * pow(SphP[i].Density, GAMMA_MINUS1));
#ifdef SPH_BND_PARTICLES
if(P[i].ID == 0)
{
SphP[i].DtEntropy = 0;
for(k = 0; k < 3; k++)
SphP[i].HydroAccel[k] = 0;
}
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
if (SphP[i].VelocityDispersion[0] != 0)
{
/* compute sigma r */
v1m = SphP[i].VelocityDispersion[1]/SphP[i].VelocityDispersion[0];
v2m = SphP[i].VelocityDispersion[2]/SphP[i].VelocityDispersion[0];
if (v2m > v1m*v1m)
SphP[i].OptVar1 = sqrt(v2m - v1m*v1m);
else
SphP[i].OptVar1 = 0.0;
}
else
SphP[i].OptVar1 = 0.0;
#endif
#ifdef OUTPUT_CONDUCTIVITY
SphP[i].OptVar2*= GAMMA_MINUS1 / (hubble_a2 * pow(SphP[i].Density, GAMMA_MINUS1)); /* to dA/dt */
if (SphP[i].OptVar2!=0)
SphP[i].OptVar2=SphP[i].Entropy/fabs(SphP[i].OptVar2); /* to time*/
else
SphP[i].OptVar2=0;
#endif
}
tend = second();
timecomp += timediff(tstart, tend);
/* collect some timing information */
MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if(ThisTask == 0)
{
All.CPU_HydCompWalk += sumt / NTask;
All.CPU_HydCommSumm += sumcomm / NTask;
All.CPU_HydImbalance += sumimbalance / NTask;
}
+
+
+#ifdef DETAILED_CPU_OUTPUT_IN_HYDRA
+ numlist = malloc(sizeof(int) * NTask);
+ timecomplist = malloc(sizeof(double) * NTask);
+ timecommsummlist = malloc(sizeof(double) * NTask);
+ timeimbalancelist = malloc(sizeof(double) * NTask);
+
+
+ MPI_Gather(&NumSphUpdate, 1, MPI_INT, numlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecomp, 1, MPI_DOUBLE, timecomplist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecommsumm, 1, MPI_DOUBLE, timecommsummlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timeimbalance, 1, MPI_DOUBLE, timeimbalancelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+
+ if(ThisTask == 0)
+ {
+ fprintf(FdTimings, "\n hydra\n\n");
+
+ fprintf(FdTimings, "Nupdate ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12d ",numlist[i]); /* nombre de part par proc */
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecomp ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecomplist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecommsumm ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecommsummlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timeimbalance ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timeimbalancelist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "\n");
+ }
+
+ free(timeimbalancelist);
+ free(timecommsummlist);
+ free(timecomplist);
+ free(numlist);
+#endif
+
+
}
/*! This function is the 'core' of the SPH force computation. A target
* particle is specified which may either be local, or reside in the
* communication buffer.
*/
void hydro_evaluate(int target, int mode)
{
int j, k, n, timestep, startnode, numngb;
FLOAT *pos, *vel;
FLOAT mass, h_i, dhsmlDensityFactor, rho, pressure, f1, f2;
double acc[3], dtEntropy, maxSignalVel;
double dx, dy, dz, dvx, dvy, dvz;
double h_i2, hinv=1, hinv4;
double p_over_rho2_i, p_over_rho2_j, soundspeed_i, soundspeed_j;
double hfc, dwk_i, vdotr, vdotr2, visc, mu_ij, rho_ij=0, vsig;
double h_j, dwk_j, r, r2, u=0, hfc_visc;
int phase=0;
#ifdef FEEDBACK
int EnergySN;
double wk,wk_i,wk_j,uintspec,hinv3;
double dtEgySpecFeedback=0;
#endif
#ifdef COMPUTE_VELOCITY_DISPERSION
double VelocityDispersion[VELOCITY_DISPERSION_SIZE];
for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
VelocityDispersion[k]=0.0;
#endif
#ifndef NOVISCOSITYLIMITER
double dt;
#endif
#ifdef ART_CONDUCTIVITY
double hfc_cond,vsig_u,vsig_u_max;
double Arho_i,Arho_j;
double u_i,u_j;
double normGradEnergyInt_i, normGradEnergyInt_j;
double dtEntropy_artcond;
#endif
if(mode == 0)
{
pos = P[target].Pos;
vel = SphP[target].VelPred;
h_i = SphP[target].Hsml;
#ifdef FEEDBACK
EnergySN = SphP[target].EnergySN;
#endif
mass = P[target].Mass;
dhsmlDensityFactor = SphP[target].DhsmlDensityFactor;
rho = SphP[target].Density;
pressure = SphP[target].Pressure;
timestep = P[target].Ti_endstep - P[target].Ti_begstep;
soundspeed_i = sqrt(GAMMA * pressure / rho);
f1 = fabs(SphP[target].DivVel) /
(fabs(SphP[target].DivVel) + SphP[target].CurlVel +
0.0001 * soundspeed_i / SphP[target].Hsml / fac_mu);
#ifdef ART_CONDUCTIVITY
normGradEnergyInt_i = sqrt( pow(SphP[target].GradEnergyInt[0],2)+pow(SphP[target].GradEnergyInt[1],2)+pow(SphP[target].GradEnergyInt[2],2));
#endif
}
else
{
pos = HydroDataGet[target].Pos;
vel = HydroDataGet[target].Vel;
h_i = HydroDataGet[target].Hsml;
#ifdef FEEDBACK
EnergySN = HydroDataGet[target].EnergySN;
#endif
mass = HydroDataGet[target].Mass;
dhsmlDensityFactor = HydroDataGet[target].DhsmlDensityFactor;
rho = HydroDataGet[target].Density;
pressure = HydroDataGet[target].Pressure;
timestep = HydroDataGet[target].Timestep;
soundspeed_i = sqrt(GAMMA * pressure / rho);
f1 = HydroDataGet[target].F1;
#ifdef ART_CONDUCTIVITY
normGradEnergyInt_i = HydroDataGet[target].NormGradEnergyInt;
#endif
}
/* initialize variables before SPH loop is started */
acc[0] = acc[1] = acc[2] = dtEntropy = 0;
maxSignalVel = 0;
#ifdef FEEDBACK
dtEgySpecFeedback=0;
#endif
p_over_rho2_i = pressure / (rho * rho) * dhsmlDensityFactor;
h_i2 = h_i * h_i;
#ifdef ART_CONDUCTIVITY
Arho_i = pressure/rho;
u_i = pressure/(rho*GAMMA_MINUS1);
dtEntropy_artcond=0;
#endif
/* Now start the actual SPH computation for this particle */
startnode = All.MaxPart;
do
{
numngb = ngb_treefind_pairs(&pos[0], h_i, phase, &startnode);
for(n = 0; n < numngb; n++)
{
j = Ngblist[n];
dx = pos[0] - P[j].Pos[0];
dy = pos[1] - P[j].Pos[1];
dz = pos[2] - P[j].Pos[2];
#ifdef PERIODIC /* find the closest image in the given box size */
if(dx > boxHalf_X)
dx -= boxSize_X;
if(dx < -boxHalf_X)
dx += boxSize_X;
if(dy > boxHalf_Y)
dy -= boxSize_Y;
if(dy < -boxHalf_Y)
dy += boxSize_Y;
if(dz > boxHalf_Z)
dz -= boxSize_Z;
if(dz < -boxHalf_Z)
dz += boxSize_Z;
#endif
r2 = dx * dx + dy * dy + dz * dz;
h_j = SphP[j].Hsml;
if(r2 < h_i2 || r2 < h_j * h_j)
{
r = sqrt(r2);
if(r > 0)
{
p_over_rho2_j = SphP[j].Pressure / (SphP[j].Density * SphP[j].Density);
soundspeed_j = sqrt(GAMMA * p_over_rho2_j * SphP[j].Density);
dvx = vel[0] - SphP[j].VelPred[0];
dvy = vel[1] - SphP[j].VelPred[1];
dvz = vel[2] - SphP[j].VelPred[2];
vdotr = dx * dvx + dy * dvy + dz * dvz;
if(All.ComovingIntegrationOn)
vdotr2 = vdotr + hubble_a2 * r2;
else
vdotr2 = vdotr;
if(r2 < h_i2)
{
hinv = 1.0 / h_i;
#ifndef TWODIMS
hinv4 = hinv * hinv * hinv * hinv;
#else
hinv4 = hinv * hinv * hinv / boxSize_Z;
#endif
u = r * hinv;
if(u < 0.5)
dwk_i = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4);
else
dwk_i = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u);
}
else
{
dwk_i = 0;
}
if(r2 < h_j * h_j)
{
hinv = 1.0 / h_j;
#ifndef TWODIMS
hinv4 = hinv * hinv * hinv * hinv;
#else
hinv4 = hinv * hinv * hinv / boxSize_Z;
#endif
u = r * hinv;
if(u < 0.5)
dwk_j = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4);
else
dwk_j = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u);
}
else
{
dwk_j = 0;
}
if(soundspeed_i + soundspeed_j > maxSignalVel)
maxSignalVel = soundspeed_i + soundspeed_j;
if(vdotr2 < 0) /* ... artificial viscosity */
{
mu_ij = fac_mu * vdotr2 / r; /* note: this is negative! */
vsig = soundspeed_i + soundspeed_j - 3 * mu_ij;
if(vsig > maxSignalVel)
maxSignalVel = vsig;
rho_ij = 0.5 * (rho + SphP[j].Density);
f2 =
fabs(SphP[j].DivVel) / (fabs(SphP[j].DivVel) + SphP[j].CurlVel +
0.0001 * soundspeed_j / fac_mu / SphP[j].Hsml);
visc = 0.25 * All.ArtBulkViscConst * vsig * (-mu_ij) / rho_ij * (f1 + f2);
/* .... end artificial viscosity evaluation */
#ifndef NOVISCOSITYLIMITER
/* make sure that viscous acceleration is not too large */
dt = imax(timestep, (P[j].Ti_endstep - P[j].Ti_begstep)) * All.Timebase_interval;
if(dt > 0 && (dwk_i + dwk_j) < 0)
{
visc = dmin(visc, 0.5 * fac_vsic_fix * vdotr2 /
(0.5 * (mass + P[j].Mass) * (dwk_i + dwk_j) * r * dt));
}
#endif
}
else
visc = 0;
p_over_rho2_j *= SphP[j].DhsmlDensityFactor;
hfc_visc = 0.5 * P[j].Mass * visc * (dwk_i + dwk_j) / r;
hfc = hfc_visc + P[j].Mass * (p_over_rho2_i * dwk_i + p_over_rho2_j * dwk_j) / r;
acc[0] -= hfc * dx;
acc[1] -= hfc * dy;
acc[2] -= hfc * dz;
dtEntropy += 0.5 * hfc_visc * vdotr2;
#ifdef ART_CONDUCTIVITY
mu_ij = fac_mu * vdotr2 / r;
vsig_u = soundspeed_i + soundspeed_j - 3 * mu_ij;
Arho_j = SphP[j].Pressure / SphP[j].Density;
rho_ij = 0.5 * (rho + SphP[j].Density);
/* switch */
normGradEnergyInt_j = sqrt( pow(SphP[j].GradEnergyInt[0],2)+pow(SphP[j].GradEnergyInt[1],2)+pow(SphP[j].GradEnergyInt[2],2));
u_j = SphP[j].Pressure/(SphP[j].Density*GAMMA_MINUS1);
hfc_cond = P[j].Mass * All.ArtCondConst * vsig_u * (Arho_i-Arho_j)/ rho_ij * 0.5*(dwk_i + dwk_j);
dtEntropy_artcond = hfc_cond / GAMMA_MINUS1;
dtEntropy += dtEntropy_artcond ;
#endif
/*****************************************/
/* FEEDBACK INTERACTION */
/*****************************************/
#ifdef FEEDBACK
rho_ij = 0.5 * (rho + SphP[j].Density);
if(P[j].Ti_endstep == All.Ti_Current)
{
/* additional feedback entropy */
if ((EnergySN > 0)||(SphP[j].EnergySN > 0))
{
/* find the thermal specific energy to release */
uintspec = 0.;
if (EnergySN > 0)
{
uintspec = 0;
}
if (SphP[j].EnergySN > 0)
{
uintspec += SphP[j].EnergySN * (1-All.SupernovaFractionInEgyKin)/ mass;
}
if(r2 < h_i2)
{
hinv = 1.0 / h_i;
hinv3 = hinv * hinv * hinv ;
u = r * hinv;
if(u < 0.5)
wk_i = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u);
else
wk_i = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u);
}
else
wk_i = 0;
if(r2 < h_j * h_j)
{
hinv = 1.0 / h_j;
hinv3 = hinv * hinv * hinv ;
u = r * hinv;
if(u < 0.5)
wk_j = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u);
else
wk_j = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u);
}
else
wk_j = 0;
wk = 0.5*(wk_i+wk_j);
/* dt in physical units */
dt = imax(timestep, (P[j].Ti_endstep - P[j].Ti_begstep)) * All.Timebase_interval/ hubble_a;
if(dt <= 0);
uintspec = 0;
/* thermal feedback */
uintspec = (uintspec/dt) *wk *0.5*(mass + P[j].Mass) /rho_ij *fac_pow ;
dtEntropy += uintspec;
dtEgySpecFeedback += uintspec;
}
}
#endif /* FEEDBACK */
}
}
}
}
while(startnode >= 0);
/* Now collect the result at the right place */
if(mode == 0)
{
for(k = 0; k < 3; k++)
SphP[target].HydroAccel[k] = acc[k];
SphP[target].DtEntropy = dtEntropy;
#ifdef FEEDBACK
SphP[target].DtEgySpecFeedback = dtEgySpecFeedback;
#endif
SphP[target].MaxSignalVel = maxSignalVel;
#ifdef COMPUTE_VELOCITY_DISPERSION
for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
SphP[target].VelocityDispersion[k] = VelocityDispersion[k];
#endif
#ifdef OUTPUT_CONDUCTIVITY
SphP[target].OptVar2 = dtEntropy_artcond;
#endif
}
else
{
for(k = 0; k < 3; k++)
HydroDataResult[target].Acc[k] = acc[k];
HydroDataResult[target].DtEntropy = dtEntropy;
#ifdef FEEDBACK
HydroDataResult[target].DtEgySpecFeedback = dtEgySpecFeedback;
#endif
HydroDataResult[target].MaxSignalVel = maxSignalVel;
#ifdef COMPUTE_VELOCITY_DISPERSION
for(k = 0; k < VELOCITY_DISPERSION_SIZE; k++)
HydroDataResult[target].VelocityDispersion[k] = VelocityDispersion[k];
#endif
#ifdef OUTPUT_CONDUCTIVITY
HydroDataResult[target].OptVar2 = dtEntropy_artcond;
#endif
}
}
/*! This is a comparison kernel for a sort routine, which is used to group
* particles that are going to be exported to the same CPU.
*/
int hydro_compare_key(const void *a, const void *b)
{
if(((struct hydrodata_in *) a)->Task < (((struct hydrodata_in *) b)->Task))
return -1;
if(((struct hydrodata_in *) a)->Task > (((struct hydrodata_in *) b)->Task))
return +1;
return 0;
}
diff --git a/main.c b/main.c
index 2c46daa..553ad41 100644
--- a/main.c
+++ b/main.c
@@ -1,874 +1,881 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include "allvars.h"
#include "proto.h"
/*! \file main.c
* \brief start of the program
*/
/*!
* This function initializes the MPI communication packages, and sets
* cpu-time counters to 0. Then begrun() is called, which sets up
* the simulation either from IC's or from restart files. Finally,
* run() is started, the main simulation loop, which iterates over
* the timesteps.
*/
int main(int argc, char **argv)
{
double t0, t1;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &ThisTask);
MPI_Comm_size(MPI_COMM_WORLD, &NTask);
if(NTask <= 1)
{
if(ThisTask == 0)
printf
("Note: This is a massively parallel code, but you are running with 1 processor only.\nCompared to an equivalent serial code, there is some unnecessary overhead.\n");
}
for(PTask = 0; NTask > (1 << PTask); PTask++);
if(argc < 2)
{
if(ThisTask == 0)
{
printf("Parameters are missing.\n");
printf("Call with <ParameterFile> [<RestartFlag>]\n");
}
endrun(0);
}
strcpy(ParameterFile, argv[1]);
if(argc >= 3)
RestartFlag = atoi(argv[2]);
else
RestartFlag = 0;
All.CPU_TreeConstruction = All.CPU_TreeWalk = All.CPU_Gravity = All.CPU_Potential = All.CPU_Domain =
All.CPU_Snapshot = All.CPU_Total = All.CPU_CommSum = All.CPU_Imbalance = All.CPU_Hydro =
All.CPU_HydCompWalk = All.CPU_HydCommSumm = All.CPU_HydImbalance =
All.CPU_EnsureNgb = All.CPU_Predict = All.CPU_TimeLine = All.CPU_PM = All.CPU_Peano = 0;
#ifdef COOLING
All.CPU_Cooling = 0;
#endif
#ifdef SFR
All.CPU_StarFormation = 0;
#endif
#ifdef CHIMIE
All.CPU_Chimie = 0;
+ All.CPU_ChimieDensCompWalk = 0;
+ All.CPU_ChimieDensCommSumm = 0;
+ All.CPU_ChimieDensImbalance = 0;
+ All.CPU_ChimieDensEnsureNgb = 0;
+ All.CPU_ChimieCompWalk = 0;
+ All.CPU_ChimieCommSumm = 0;
+ All.CPU_ChimieImbalance = 0;
#endif
#ifdef MULTIPHASE
All.CPU_Sticky = 0;
#endif
#ifdef DETAILED_CPU_DOMAIN
All.CPU_Domain_findExtend = 0;
All.CPU_Domain_determineTopTree = 0;
All.CPU_Domain_sumCost = 0;
All.CPU_Domain_findSplit = 0;
All.CPU_Domain_shiftSplit = 0;
All.CPU_Domain_countToGo = 0;
All.CPU_Domain_exchange = 0;
#endif
#ifdef DETAILED_CPU_GRAVITY
All.CPU_Gravity_TreeWalk1 = 0;
All.CPU_Gravity_TreeWalk2 = 0;
All.CPU_Gravity_CommSum1 = 0;
All.CPU_Gravity_CommSum2 = 0;
All.CPU_Gravity_Imbalance1 = 0;
All.CPU_Gravity_Imbalance2 = 0;
#endif
#ifdef DETAILED_CPU
All.CPU_Leapfrog = 0;
All.CPU_Physics = 0;
All.CPU_Residual = 0;
All.CPU_Accel = 0;
All.CPU_Begrun = 0;
#endif
CPUThisRun = 0;
t0 = second();
begrun(); /* set-up run */
t1 = second();
CPUThisRun += timediff(t0, t1);
All.CPU_Total += timediff(t0, t1);
run(); /* main simulation loop */
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'.
----------------------------------------------------------------------
*/
/*! \mainpage Reference documentation for GADGET-2
\author Volker Springel \n
Max-Planck-Institute for Astrophysics \n
Karl-Schwarzschild-Str. 1 \n
85740 Garching \n
Germany \n
volker@mpa-garching.mpg.de \n
\n
\section prelim Getting started
GADGET-2 is a massively parallel code for hydrodynamical cosmological
simulations. It is a flexible code that can be applied to a variety of
different types of simulations, offering a number of sophisticated
simulation algorithms.
A full account of the numerical algorithms employed by the code is given
in the accompanying code paper, and detailed instructions for usage of
the code are given in the included code documentation.
This html-document serves as a cross-referenced documentation of the
source code itself - in fact, using the doxygen tool, the html-pages
have been produced from comments inlined in the source code. Apart from
the source-code documentation, a brief guide to code compilation is
given below, and under <b>Related Pages (see link on top)</b> you can find an
explanation of GADGET's parameterfile and a short guide to compile-time
options of the code.
\section install Compilation
GADGET-2 needs the following non-standard libraries for compilation:
- \b MPI - the Message Passing Interface (version 1.0 or higher). Many
vendor supplied versions exist, in addition to excellent open source
implementations, e.g. MPICH
(http://www-unix.mcs.anl.gov/mpi/mpich/) or LAM
(http://www.lam-mpi.org/).
- \b GSL - the GNU scientific library. This open-source package can be
obtained at http://www.gnu.org/software/gsl , for example. GADGET-2
needs this library for a few simple cosmological
integrations at start-up, and for random number generation.
- \b HDF5 - the <em>Hierarchical Data Format</em>. This library has been
developed by NCSA and can be obtained at http://hdf.ncsa.uiuc.edu/HDF5 .
GADGET-2 can be compiled without this library, but then the HDF5 format
is not supported.
- \b FFTW - the <em>Fastest Fourier Transform in the West</em>. This
open-source package can be obtained at http://www.fftw.org . It is only
needed for simulations that use the TreePM algorithm. Note that the
MPI-capable version 2.x of FFTW is required, and that FFTW needs to be
explicitly compiled with parallel support enabled. This can be achieved
by passing the option <b>--enable-mpi</b> to the configure script. When
at it, you might as well add <b>--enable-type-prefix</b> to obtain the
libraries in both a single and double precision version. If this has not
been done, you should set the option <em>NOTYPEPREFIX_FFTW</em> in GADGET's
\ref Gadget-Makefile "Makefile".
Note that if any of the above libraries is not installed in standard
locations on your system, the \ref Gadget-Makefile "Makefile" provided with
the code may need slight adjustments. Similarly, compiler options,
particularly with respect to optimisations, may need adjustment to the
C-compiler that is used. Finally, the \ref Gadget-Makefile "Makefile"
contains a number of compile-time options that need to be set appropriately
for the type of simulation that is simulated.
The provided makefile is compatible with GNU-make, i.e. typing \b make or
\b gmake should then build the executable <b>Gadget2</b>. If your site
does not have GNU-make, get it, or write your own makefile.
\section howtorun Running the code
In order to start the simulation code, a \ref parameterfile "parameterfile"
needs to be specified. An additional optional numerical parameter can be
used to signal whether a continuation from a set of restart files, or from
a snapshot file, is desired. A typical command to start the code looks like
the following: \n \n
<b> mpirun -np 8 ./Gadget2 <parameterfile> [restartflag]</b> \n \n
This would start the code using 8 processors, assuming that the parallel
environment uses the <em>mpirun</em> command to start MPI
applications. Depending on the operating system, other commands may be
required for this task, e.g. <em>poe</em> on IBM/AIX machines. Note that
the code can in principle be started using an arbitrary number of
processors, but the communication algorithms will be most efficient for
powers of 2. It is also possible to use a single processor only, in
which case the code behaves like a serial code, except that GADGET-2
will still go through some of the overhead induced by the
parallelization algorithms, so the code will not quite reach the same
performance as an optimum serial solution in this case.
The optional <em>restartflag</em> can have the values 0, 1, or 2, only. "1"
signals a continuation from restart files, while "2" can be used to restart
from a snapshot file produced by the code. If omitted (equivalent to the
default of "0"), the code starts from initial conditions.
*/
/*! \page parameterfile Parameterfile of GADGET-2
The parameterfile for GADGET-2 is a simple text file, consisting of pairs of
tags and values. For each parameter, a separate line needs to be specified,
first listing the name (tag) of the parameter, and then the assigned value,
separated by whitespace. It is allowed to add further text behind the
assigned parameter value. The order of the parameters is arbitrary, but
each one needs to occur exactly one time, otherwise an error message will
be produced. Empty lines, or lines beginning with a \%-sign, are ignored and
treated as comments.
- \b InitCondFile \n The filename of the initial conditions file. If a
restart from a snapshot with the "2" option is desired, one
needs to specify the snapshot file here.
- \b OutputDir \n Pathname of the output directory of the code.
- \b EnergyFile \n Filename of the log-file that contain the energy
statistics.
- \b InfoFile \n Log-file that contains a list of the timesteps taken.
- \b TimingsFile \n Log-file with performance metrics of the gravitational
tree computation.
- \b CpuFile \n Log-file with CPU time consumption in various parts of the
code.
- \b RestartFile \n Basename of restart-files produced by the code.
- \b SnapshotFileBase \n Basename of snapshot files produced by the code.
- \b OutputListFilename \n File with a list of the desired output times.
- \b TimeLimitCPU \n CPU-time limit for the present submission of the
code. If 85 percent of this time have been reached at the end
of a timestep, the code terminates itself and produces restart
files.
- \b ResubmitOn \n If set to "1", the code will try to resubmit itself to
the queuing system when an interruption of the run due to the
CPU-time limit occurs. The resubmission itself is done by
executing the program/script given with
<em>ResubmitCommand</em>.
- \b ResubmitCommand \n The name of a script file or program that is
executed for automatic resubmission of the job to the queuing
system. Note that the file given here needs to be executable.
- \b ICFormat \n The file format of the initial conditions. Currently,
three different formats are supported, selected by one of the
choices "1", "2", or "3". Format "1" is the traditional
fortran-style unformatted format familiar from
GADGET-1. Format "2" is a variant of this format, where each
block of data is preceeded by a 4-character
block-identifier. Finally, format "3" selects the HDF-5
format.
- \b SnapFormat \n Similar as <em>ICFormat</em>, this parameter selects the
file-format of snapshot dumps produced by the code.
- \b ComovingIntegrationOn \n If set to "1", the code assumes that a
cosmological integration in comoving coordinates is carried
out, otherwise ordinary Newtonian dynamics is assumed.
- \b TypeOfTimestepCriterion \n This parameter can in principle be used to
select different kinds of timestep criteria for gravitational
dynamics. However, GADGET-2 presently only supports the
standard criterion "0".
- \b OutputListOn \n If set to "1", the code tries to read a list of
desired output times from the file given in
<em>OutputListFilename</em>. Otherwise, output times are
generated equally spaced from the values assigned for
<em>TimeOfFirstSnapshot</em> and <em>TimeBetSnapshot</em>.
- \b PeriodicBoundariesOn \n If set to "1", periodic boundary conditions
are assumed, with a cubical box-size of side-length
<em>BoxSize</em>. Particle coordinates are expected to be in
the range [0,<em>BoxSize</em>[.
- \b TimeBegin \n This sets the starting time of a simulation when the code
is started from initial conditions. For cosmological
integrations, the value specified here is taken as the initial
scale factor.
- \b TimeMax \n This sets the final time for the simulation. The code
normally tries to run until this time is reached. For
cosmological integrations, the value given here is the final
scale factor.
- \b Omega0 \n Gives the total matter density (in units of the critical
density) at z=0 for cosmological simulations.
- \b OmegaLambda \n Gives the vacuum energy density at z=0 for cosmological
simulations.
- \b OmegaBaryon \n Gives the baryon density at z=0 for cosmological
simulations.
- \b HubbleParam \n This gives the Hubble constant at z=0 in units of 100
km/sec/Mpc. Note that this parameter has been basically
absorbed into the definition of the internal code units, such
that for gravitational dynamics and adiabatic gas dynamics the
actual value assigned for <em>HubbleParam</em> is not used by
the code.
- \b BoxSize \n The boxsize for simulations with periodic boundary
conditions.
- \b TimeOfFirstSnapshot \n The time of the first desired snapshot file in
case a file with output times is not specified. For
cosmological simulations, the value given here is the scale factor
of the first desired output.
- \b TimeBetSnapshot \n The time interval between two subsequent snapshot
files in case a file with output times is not specified. For
cosmological simulations, this is a multiplicative factor
applied to the time of the last snapshot, such that the
snapshots will have a constant logarithmic spacing in the
scale factor. Otherwise, the parameter is an additive constant
that gives the linear spacing between snapshot times.
- \b CpuTimeBetRestartFile \n The value specfied here gives the time in
seconds the code will run before it writes regularly produced
restart files. This can be useful to protect against
unexpected interruptions (for example due to a hardware
problem) of a simulation, particularly if it is run for a long
time. It is then possible to resume a simulation from the last
restart file, reducing the potential loss to the elapsed
CPU-time since this was produced.
- \b TimeBetStatistics \n The code can be asked to measure the total
kinetic, thermal, and potential energy in regular intervals,
and to write the results to the file given in
<em>EnergyFile</em>. The time interval between two such
measurements is given by the parameter
<em>TimeBetStatistics</em>, in an analogous way as with
<em>TimeBetSnapshot</em>. Note that the compile time option
<em>COMPUTE_POTENTIAL_ENERGY</em> needs to be activated to
obtain a measurement of the gravitational potential energy.
- \b NumFilesPerSnapshot \n The number of separate files requested for each
snapshot dump. Each file of the snapshot will hold the data of
one or several processors, up to all of
them. <em>NumFilesPerSnapshot</em> must hence lie between 1
and the number of processors used. Distributing a snapshot
onto several files can be done in parallel and may lead to
much better I/O performance, depending on the hardware
configuration. It can also help to avoid problems due to big
files (>2GB) for large simulations. Note that initial
conditions may also be distributed into several files, the
number of which is automatically recognised by the code and
does not have to be equal to <em>NumFilesPerSnapshot</em> (it
may also be larger than the number of processors).
- \b NumFilesWrittenInParallel \n The number of files the code may read or
write simultaneously when writing or reading snapshot/restart
files. The value of this parameter must be smaller or equal to
the number of processors.
- \b ErrTolIntAccuracy \n This dimensionless parameter controls the
accuracy of the timestep criterion selected by
<em>TypeOfTimestepCriterion</em>.
- \b CourantFac \n This sets the value of the Courant parameter used in the
determination of the hydrodynamical timestep of SPH particles.
- \b MaxSizeTimestep \n This gives the maximum timestep a particle may
take. This should be set to a sensible value in order to
protect against too large timesteps for particles with very
small acceleration. For cosmological simulations, the
parameter given here is the maximum allowed step in the
logarithm of the expansion factor. Note that the definition
of MaxSizeTimestep has <em>changed</em> compared to Gadget-1.1 for cosmological simulations.
- \b MinSizeTimestep \n If a particle requests a timestep smaller than the
value specified here, the code will normally terminate with a
warning message. If compiled with the
<em>NOSTOP_WHEN_BELOW_MINTIMESTEP</em> option, the code will
instead force the timesteps to be at least as large as
<em>MinSizeTimestep</em>.
- \b TypeOfOpeningCriterion \n This selects the type of cell-opening
criterion used in the tree walks. A value of `0' results in
standard Barnes & Hut, while `1' selects the relative opening
criterion of GADGET-2.
- \b ErrTolTheta \n This gives the maximum opening angle if the BH
criterion is used for the tree walk. If the relative opening
criterion is used instead, a first force estimate is computed
using the BH algorithm, which is then recomputed with the
relative opening criterion.
- \b ErrTolForceAcc \n The accuracy parameter for the relative opening
criterion for the tree walk.
- \b TreeDomainUpdateFrequency \n The domain decomposition and tree
construction need not necessarily be done every single
timestep. Instead, tree nodes can be dynamically updated,
which is faster. However, the tree walk will become more
expensive since the tree nodes have to "grow" to keep
accomodating all particles they enclose. The parameter
<em>TreeDomainUpdateFrequency</em> controls how often the
domain decomposition is carried out and the tree is
reconstructed from scratch. For example, a value of 0.1 means
that the domain decomposition and the tree are reconstructed
whenever there have been more than 0.1*N force computations
since the last reconstruction, where N is the total particle
number. A value of 0 will reconstruct the tree every timestep.
- \b MaxRMSDisplacementFac \n This parameter is an additional timestep
criterion for the long-range integration in case the TreePM
algorithm is used. It limits the long-range timestep such that
the rms-displacement of particles per step is at most
<em>MaxRMSDisplacementFac</em> times the mean
particle separation, or the mesh-scale, whichever is smaller.
- \b DesNumNgb \n This sets the desired number of SPH smoothing neighbours.
- \b MaxNumNgbDeviation \n This sets the allowed variation of the number of
neighbours around the target value <em>DesNumNgb</em>.
- \b ArtBulkViscConst \n This sets the value of the artificial viscosity
parameter used by GADGET-2.
- \b InitGasTemp \n This sets the initial gas temperature (assuming either
a mean molecular weight corresponding to full ionization or
full neutrality, depending on whether the temperature is above
or below 10^4 K) in Kelvin when initial conditions are
read. However, the gas temperature is only set to a certain
temperature if <em>InitGasTemp</em>>0, and if the temperature
of the gas particles in the initial conditions file is zero,
otherwise the initial gas temperature is left at the value
stored in the IC file.
- \b MinGasTemp \n A minimum temperature floor imposed by the code. This
may be set to zero.
- \b PartAllocFactor \n Each processor allocates space for
<em>PartAllocFactor</em> times the average number of particles
per processor. This number needs to be larger than 1 to allow
the simulation to achieve a good work-load balancing, which
requires to trade particle-load balance for work-load
balance. It is good to make <em>PartAllocFactor</em> quite a
bit larger than 1, but values in excess of 3 will typically
not improve performance any more. For a value that is too
small, the code may not be able to succeed in the domain
decomposition and terminate.
- \b TreeAllocFactor \n To construct the BH-tree for N particles, somewhat
less than N internal tree-nodes are necessary for `normal'
particle distributions. <em>TreeAllocFactor</em> sets the
number of internal tree-nodes allocated in units of the
particle number. By experience, space for 0.65 N internal
nodes is usually fully sufficient, so a value of 0.7 should
put you on the safe side.
- \b BufferSize \n This specifies the size (in MByte per processor) of a
communication buffer used by the code.
- \b UnitLength_in_cm \n This sets the internal length unit in cm/h, where
H_0 = 100 h km/sec/Mpc. For example, a choice of 3.085678e21
sets the length unit to 1.0 kpc/h.
- \b UnitMass_in_g \n This sets the internal mass unit in g/h, where H_0 =
100 h km/sec/Mpc. For example, a choice of 1.989e43 sets the
mass unit to 10^10 M_sun/h.
- \b UnitVelocity_in_cm_per_s \n This sets the internal velocity unit in
cm/sec. For example, a choice of 1e5 sets the velocity unit to
km/sec. Note that the specification of
<em>UnitLength_in_cm</em>, <em>UnitMass_in_g</em>, and
<em>UnitVelocity_in_cm_per_s</em> also determines the internal
unit of time.
- \b GravityConstantInternal \n The numerical value of the gravitational
constant G in internal units depends on the system of units
you choose. For example, for the choices above, G=43007.1 in
internal units. For <em>GravityConstantInternal</em>=0, the
code calculates the value corresponding to the physical value
of G automatically. However, you might want to set G
yourself. For example, by specifying
<em>GravityConstantInternal</em>=1,
<em>UnitLength_in_cm</em>=1, <em>UnitMass_in_g</em>=1, and
<em>UnitVelocity_in_cm_per_s</em>=1, one obtains a `natural'
system of units. Note that the code will nevertheless try to
use the `correct' value of the Hubble constant in this case,
so you should not set <em>GravityConstantInternal</em> in
cosmological integrations.
- \b MinGasHsmlFractional \n This parameter sets the minimum allowed SPH
smoothing length in units of the gravitational softening
length of the gas particles. The smoothing length will be
prevented from falling below this value. When this bound is
actually reached, the number of smoothing neighbors will
instead be increased above <em>DesNumNgb</em>.
- \b SofteningGas \n The Plummer equivalent gravitational softening length
for particle type 0, which are the gas particles. For
cosmological simulations in comoving coordinates, this is
interpreted as a comoving softening length.
- \b SofteningHalo \n The Plummer equivalent gravitational softening length
for particle type 1.
- \b SofteningDisk \n The Plummer equivalent gravitational softening length
for particle type 2.
- \b SofteningBulge \n The Plummer equivalent gravitational softening
length for particle type 3.
- \b SofteningStars \n The Plummer equivalent gravitational softening
length for particle type 4.
- \b SofteningBndry \n The Plummer equivalent gravitational softening
length for particle type 5.
- \b SofteningGasMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 0. Depening on the relative settings
of <em>SofteningGas</em> and <em>SofteningGasMaxPhys</em>, the
code will hence switch from a softening constant in comoving
units to one constant in physical units.
- \b SofteningHaloMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 1.
- \b SofteningDiskMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 2.
- \b SofteningBulgeMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 3.
- \b SofteningStarsMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 4.
- \b SofteningBndryMaxPhys \n When comoving integration is used, this
parameter gives the maximum physical gravitational softening
length for particle type 5.
*/
/*! \page Gadget-Makefile Makefile of GADGET-2
A number of features of GADGET-2 are controlled with compile-time options
in the makefile rather than by the parameterfile. This has been done in
order to allow the generation of highly optimised binaries by the compiler,
even when the underlying source code allows for many different ways to run the
code.
The makefile contains a dummy list of all available compile-time options,
with most of them commented out by default. To activate a certain feature,
the corresponding parameter should be commented in, and given the desired
value, where appropriate. Below, a brief guide to these options is
included.
<b>Important Note:</b> Whenever one of the compile-time options
described below is modified, a full recompilation of the code may be
necessary. To guarantee that this is done when a simple <b>make</b> is
specified, all source files have been specified in the Makefile as being
dependent on the Makefile itself. Alternatively, one can also issue the
command <b>make clean</b>, which will erase all object files, followed
by <b>make</b>.
Note that the above technique has the disadvantage that different
simulations may require different binaries of GADGET-2. If several
simulations are run concurrently, there is hence the danger that a
simulation is started/resumed with the `wrong' binary. Note that while
GADGET-2 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 executable for a simulation, it is recommended to produce a
separate executable for each simulation that is run. For example, a good
strategy is to make a copy of the whole code together with its makefile in
the output directory of each simulation run, and then to use this copy to
compile the code and to run the simulation.
\n
\section secmake1 Basic operation mode of code
- \b PERIODIC \n Set this if you want to have periodic boundary conditions.
- \b UNEQUALSOFTENINGS \n Set this if you use particles with different
gravitational softening lengths.
\n
\section secmake2 Things that are always recommended
- \b PEANOHILBERT \n This is a tuning option. When set, the code will bring
the particles into Peano-Hilbert order after each domain
decomposition. This improves cache utilisation and performance.
- \b WALLCLOCK \n If set, a wallclock timer is used by the code to measure
internal time consumption (see cpu-log file). Otherwise, a timer that
measures consumed processor ticks is used.
\n
\section secmake3 TreePM options
- \b PMGRID=128 \n This enables the TreePM method, i.e. the long-range
force is computed with a PM-algorithm, and the short range force with
the tree. The parameter has to be set to the size of the mesh that
should be used, e.g.~64, 96, 128, etc. The mesh dimensions need not
necessarily be a power of two, but the FFT is fastest for such a
choice. Note: If the simulation is not in a periodic box, then a FFT
method for vacuum boundaries is employed, using a mesh with dimension
twice that specified by <b>PMGRID</b>.
- \b PLACEHIGHRESREGION=1+8 \n If this option is set (will only work
together with \b PMGRID), then the long range force is computed in two
stages: One Fourier-grid is used to cover the whole simulation volume,
allowing the computation of the large-scale force. A second Fourier
mesh is placed on the region occupied by `high-resolution' particles,
allowing the computation of an intermediate-scale force. Finally, the
force on very small scales is computed by the tree. This procedure can
be useful for `zoom-simulations', where the majority of particles (the
high-res particles) are occupying only a small fraction of the
volume. To activate this option, the parameter needs to be set to an
integer that encodes the particle types that make up the high-res
particles in the form of a bit mask. For example, if types 0, 1, and 4
are the high-res particles, then the parameter should be set to
<b>PLACEHIGHRESREGION=1+2+16</b>, i.e. to the sum
\f$2^0+2^1+2^4\f$. The spatial region covered by the high-res grid is
determined automatically from the initial conditions. Note: If a
periodic box is used, the high-res zone is not allowed to intersect the box
boundaries.
- <b> ENLARGEREGION=1.1</b> \n The spatial region covered by the high-res zone
normally has a fixed size during the simulation, which initially is
set to the smallest region that encompasses all high-res
particles. Normally, the simulation will be interrupted if high-res
particles leave this region in the course of the run. However, by
setting this parameter to a value larger than one, the high-res region
can be expanded on the fly. For example, setting it to 1.4 will enlarge its
side-length by 40% in such an event (it remains centred on the high-res
particles). Hence, with such a setting, the high-res region may expand
or move by a limited amount. If in addition \b SYNCHRONIZATION is
activated, then the code will be able to continue even if high-res
particles leave the initial high-res grid. In this case, the code will
update the size and position of the grid that is placed onto the
high-resolution region automatically. To prevent that this potentially
happens every single PM step, one should nevertheless assign a value
slightly larger than 1 to \b ENLARGEREGION.
- <b> ASMTH=1.25</b> \n This can be used to override the value assumed for the
scale that defines the long-range/short-range force-split in the
TreePM algorithm. The default value is 1.25, in mesh-cells.
- <b> RCUT=4.5</b> \n This can be used to override the maximum radius in which
the short-range tree-force is evaluated (in case the TreePM algorithm
is used). The default value is 4.5, given in mesh-cells.
\n
\section secmake4 Single or double precision
- \b DOUBLEPRECISION \n This makes the code store and compute internal
particle data in double precision. Note that output files are
nevertheless written by converting the values that are saved to single
precision.
- \b DOUBLEPRECISION_FFTW \n If this is set, the code will use the
double-precision version of FTTW, provided the latter has been
explicitly installed with a "d" prefix, and NOTYPEPREFIX_FFTW is not
set. Otherwise the single precision version ("s" prefix) is used.
\n
\section secmake5 Time integration options
- \b SYNCHRONIZATION \n When this is set, particles may only increase their
timestep if the new timestep will put them into synchronisation with
the higher time level. This typically means that only on half of the
timesteps of a particle an increase of its step may occur. Especially
for TreePM runs, it is usually advisable to set this option.
- \b FLEXSTEPS \n This is an alternative to SYNCHRONIZATION. Particle
timesteps are here allowed to be integer multiples of the minimum
timestep that occurs among the particles, which in turn is rounded
down to the nearest power-of-two devision of the total simulated
timespan. This option distributes particles more evenly over
individual system timesteps, particularly once a simulation has run
for a while, and may then result in a reduction of work-load imbalance
losses.
- \b PSEUDOSYMMETRIC \n When this option is set, the code will try to
`anticipate' timestep changes by extrapolating the change of the
acceleration into the future. This in general improves the long-term
integration behaviour of periodic orbits, because then the adaptive
integration becomes more akin to a strictly time reversible
integrator. Note: This option has no effect if FLEXSTEPS is set.
- \b NOSTOP_WHEN_BELOW_MINTIMESTEP \n If this is activated, the code will
not terminate when the timestep falls below the value of \b
MinSizeTimestep specified in the parameterfile. This is useful for
runs where one wants to enforce a constant timestep for all
particles. This can be done by activating this option, and by setting
\b MinSizeTimestep and \b MaxSizeTimestep to an equal value.
- \b NOPMSTEPADJUSTMENT \n When this is set, the long-range timestep for
the PM force computation is always determined by \b MaxSizeTimeStep.
Otherwise, it is set to the minimum of \b MaxSizeTimeStep and the
timestep obtained for the maximum long-range force with an effective
softening scale equal to the PM smoothing-scale.
\n
\section secmake6 Output options
- \b HAVE_HDF5 \n If this is set, the code will be compiled with support
for input and output in the HDF5 format. You need to have the HDF5
libraries and headers installed on your computer for this option to
work. The HDF5 format can then be selected as format "3" in Gadget's
parameterfile.
- \b OUTPUTPOTENTIAL \n This will force the code to compute gravitational
potentials for all particles each time a snapshot file is
generated. These values are then included in the snapshot files. Note
that the computation of the values of the potential costs additional
time.
- \b OUTPUTACCELERATION \n This will include the physical acceleration of
each particle in snapshot files.
- \b OUTPUTCHANGEOFENTROPY \n This will include the rate of change of
entropy of gas particles in snapshot files.
- \b OUTPUTTIMESTEP \n This will include the timesteps actually taken by
each particle in the snapshot files.
\n
\section secmake7 Things for special behaviour
- \b NOGRAVITY \n This switches off gravity. Makes only sense for pure SPH
simulations in non-expanding space.
- \b NOTREERND \n If this is not set, the tree construction will succeed
even when there are a few particles at identical locations. This is
done by `rerouting' particles once the node-size has fallen below
\f$10^{-3}\f$ of the softening length. When this option is activated,
this will be suppressed and the tree construction will always fail if
there are particles at extremely close or identical coordinates.
- \b NOTYPEPREFIX_FFTW \n If this is set, the fftw-header/libraries are
accessed without type prefix (adopting whatever was chosen as default
at compile-time of fftw). Otherwise, the type prefix 'd' for
double-precision is used.
- \b LONG_X/Y/Z \n These options can be used together with PERIODIC and
NOGRAVITY only. When set, the options define numerical factors that
can be used to distort the periodic simulation cube into a
parallelepiped of arbitrary aspect ratio. This can be useful for
idealized SPH tests.
- \b TWODIMS \n This effectively switches of one dimension in SPH,
i.e. the code follows only 2d hydrodynamics in the xy-, yz-, or
xz-plane. This only works with NOGRAVITY, and if all coordinates of
the third axis are exactly equal. Can be useful for idealized SPH
tests.
- \b SPH_BND_PARTICLES \n If this is set, particles with a particle-ID
equal to zero do not receive any SPH acceleration. This can be useful
for idealized SPH tests, where these particles represent fixed
"walls".
- \b NOVISCOSITYLIMITER \n If this is set, there is no explicit upper
limit on the viscosity. In the default version, this limiter will
try to protect against possible particle `reflections', which could
in principle occur if very poor timestepping is used in the
presence of strong shocks.
- \b COMPUTE_POTENTIAL_ENERGY \n When this option is set, the code will
compute the gravitational potential energy each time a global
statistics is computed. This can be useful for testing global energy
conservation.
- \b ISOTHERM_EQS \n This special option makes the gas behave like an
isothermal gas with equation of state \f$ P = c_s^2 \rho \f$. The
sound-speed \f$ c_s \f$ is set by the thermal energy per unit mass
in the intial conditions, i.e. \f$ c_s^2=u \f$. If the value for \f$ u \f$
is zero, then the initial gas temperature in the parameter file is
used to define the sound speed according to \f$ c_s^2= 3/4\,
k\,T/m_p \f$ , where \f$ m_p \f$ is the proton mass.
- \b ADAPTIVE_GRAVSOFT_FORGAS \n When this option is set, the gravitational
softening lengths used for gas particles is tied to their SPH smoothing
length. This can be useful for dissipative collapse simulations. The
option requires the setting of UNEQUALSOFTENINGS.
- \b SELECTIVE_NO_GRAVITY \n This can be used for special computations where
one wants to exclude certain particle types from receiving gravitational
forces. The particle types that are excluded in this fashion are specified
by a bit mask, in the same as for the PLACEHIGHRESREGION option.
- \b LONGIDS \n If this is set, the code assumes that particle-IDs are
stored as 64-bit long integers. This is only really needed if you want
to go beyond ~2 billion particles.
\n
\section secmake8 Testing and Debugging options
- \b FORCETEST=0.01 \n This can be set to check the force accuracy of the
code, and is only included as a debugging option. The option needs to
be set to a number between 0 and 1 (e.g. 0.01), which specifies the
fraction of randomly chosen particles for which at each timestep
forces by direct summation are computed. The normal tree-forces and
the `correct' direct summation forces are then collected in a file \b
forcetest.txt for later inspection. Note that the simulation itself is
unaffected by this option, but it will of course run much(!) slower,
particularly if <b> FORCETEST*NumPart*NumPart>>NumPart</b>
Note: Particle IDs must be set to numbers >=1 for this
option to work.
\n
\section secmake9 Glass making
- \b MAKEGLASS=262144 \n This option can be used to generate a glass-like
particle configuration. The value assigned gives the particle load,
which is initially generated as a Poisson sample and then evolved
towards a glass with the sign of gravity reversed
*/
diff --git a/run.c b/run.c
index e9abe7f..0708276 100644
--- a/run.c
+++ b/run.c
@@ -1,837 +1,837 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include <unistd.h>
#include "allvars.h"
#include "proto.h"
/*! \file run.c
* \brief iterates over timesteps, main loop
*/
/*! This routine contains the main simulation loop that iterates over single
* timesteps. The loop terminates when the cpu-time limit is reached, when a
* `stop' file is found in the output directory, or when the simulation ends
* because we arrived at TimeMax.
*/
void run(void)
{
FILE *fd;
int stopflag = 0;
char stopfname[200], contfname[200];
double t0, t1;
#ifdef DETAILED_CPU
double tstart,tend;
#endif
sprintf(stopfname, "%sstop", All.OutputDir);
sprintf(contfname, "%scont", All.OutputDir);
unlink(contfname);
do /* main loop */
{
t0 = second();
find_next_sync_point_and_drift(); /* find next synchronization point and drift particles to this time.
* If needed, this function will also write an output file
* at the desired time.
*/
every_timestep_stuff(); /* write some info to log-files */
#ifdef PNBODY
compute_pnbody();
#endif
#ifdef OUTPUT_EVERY_TIMESTEP
savepositions(All.SnapshotFileCount++); /* write snapshot file */
#endif
#ifdef DETAILED_CPU
tstart = second();
#endif
#ifdef AGN_ACCRETION
compute_agn_accretion(); /* compute accretion */
#endif
#ifdef BONDI_ACCRETION
compute_bondi_accretion(); /* compute bondi accretion */
#endif
#ifdef BUBBLES
make_bubble(); /* create a bubble */
#endif
#ifdef MULTIPHASE
update_phase(); /* allow particles to change their phase */
#endif
#ifdef CORIOLIS
set_outer_potential_coriolis(); /* coriolis */
#endif
#ifdef CHIMIE
chimie();
#endif
#ifdef SFR
#ifdef COMPUTE_SFR_ENERGY
density(1);
force_update_hmax();
sfr_compute_energy_int(1);
#endif
star_formation(); /* starformation */
#endif
#ifdef MULTIPHASE
sticky();
#endif
#ifdef DETAILED_CPU
tend = second();
All.CPU_Physics += timediff(tstart, tend);
#endif
domain_Decomposition(); /* do domain decomposition if needed */
compute_accelerations(0); /* compute accelerations for
* the particles that are to be advanced
*/
#if defined(SFR) && defined(COMPUTE_SFR_ENERGY)
#ifdef DETAILED_CPU
tstart = second();
#endif
//sfr_compute_energy_int(2);
#ifdef DETAILED_CPU
tend = second();
All.CPU_Physics += timediff(tstart, tend);
#endif
#endif
/* check whether we want a full energy statistics */
if((All.Time - All.TimeLastStatistics) >= All.TimeBetStatistics)
{
#ifdef COMPUTE_POTENTIAL_ENERGY
compute_potential();
#endif
#ifndef ADVANCEDSTATISTICS
energy_statistics(); /* compute and output energy statistics */
#else
advanced_energy_statistics(); /* compute and output energy statistics */
#endif
All.TimeLastStatistics += All.TimeBetStatistics;
}
advance_and_find_timesteps(); /* 'kick' active particles in
* momentum space and compute new
* timesteps for them
*/
All.NumCurrentTiStep++;
/* Check whether we need to interrupt the run */
if(ThisTask == 0)
{
/* Is the stop-file present? If yes, interrupt the run. */
if((fd = fopen(stopfname, "r")))
{
fclose(fd);
stopflag = 1;
unlink(stopfname);
}
/* are we running out of CPU-time ? If yes, interrupt run. */
//if(CPUThisRun > 0.85 * All.TimeLimitCPU)
if(CPUThisRun > 1.0 * All.TimeLimitCPU)
{
printf("reaching time-limit. stopping.\n");
stopflag = 2;
}
}
MPI_Bcast(&stopflag, 1, MPI_INT, 0, MPI_COMM_WORLD);
if(stopflag)
{
restart(0); /* write restart file */
MPI_Barrier(MPI_COMM_WORLD);
if(stopflag == 2 && ThisTask == 0)
{
if((fd = fopen(contfname, "w")))
fclose(fd);
}
if(stopflag == 2 && All.ResubmitOn && ThisTask == 0)
{
close_outputfiles();
system(All.ResubmitCommand);
}
return;
}
/* is it time to write a regular restart-file? (for security) */
if(ThisTask == 0)
{
if((CPUThisRun - All.TimeLastRestartFile) >= All.CpuTimeBetRestartFile)
{
All.TimeLastRestartFile = CPUThisRun;
stopflag = 3;
}
else
stopflag = 0;
}
MPI_Bcast(&stopflag, 1, MPI_INT, 0, MPI_COMM_WORLD);
if(stopflag == 3)
{
restart(0); /* write an occasional restart file */
stopflag = 0;
}
t1 = second();
All.CPU_Total += timediff(t0, t1);
CPUThisRun += timediff(t0, t1);
}
while(All.Ti_Current < TIMEBASE && All.Time <= All.TimeMax);
restart(0);
savepositions(All.SnapshotFileCount++); /* write a last snapshot
* file at final time (will
* be overwritten if
* All.TimeMax is increased
* and the run is continued)
*/
}
/*! This function finds the next synchronization point of the system (i.e. the
* earliest point of time any of the particles needs a force computation),
* and drifts the system to this point of time. If the system drifts over
* the desired time of a snapshot file, the function will drift to this
* moment, generate an output, and then resume the drift.
*/
void find_next_sync_point_and_drift(void)
{
int n, min, min_glob, flag, *temp;
double timeold;
double t0, t1;
#ifdef DETAILED_CPU
double tstart,tend;
#endif
t0 = second();
#ifdef DETAILED_CPU
tstart = t0;
#endif
timeold = All.Time;
for(n = 1, min = P[0].Ti_endstep; n < NumPart; n++)
if(min > P[n].Ti_endstep)
min = P[n].Ti_endstep;
MPI_Allreduce(&min, &min_glob, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
/* We check whether this is a full step where all particles are synchronized */
flag = 1;
for(n = 0; n < NumPart; n++)
if(P[n].Ti_endstep > min_glob)
flag = 0;
MPI_Allreduce(&flag, &Flag_FullStep, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
#ifdef PMGRID
if(min_glob >= All.PM_Ti_endstep)
{
min_glob = All.PM_Ti_endstep;
Flag_FullStep = 1;
}
#endif
/* Determine 'NumForceUpdate', i.e. the number of particles on this processor that are going to be active */
for(n = 0, NumForceUpdate = 0; n < NumPart; n++)
{
if(P[n].Ti_endstep == min_glob)
#ifdef SELECTIVE_NO_GRAVITY
if(!((1 << P[n].Type) & (SELECTIVE_NO_GRAVITY)))
#endif
NumForceUpdate++;
}
/* note: NumForcesSinceLastDomainDecomp has type "long long" */
temp = malloc(NTask * sizeof(int));
MPI_Allgather(&NumForceUpdate, 1, MPI_INT, temp, 1, MPI_INT, MPI_COMM_WORLD);
for(n = 0; n < NTask; n++)
All.NumForcesSinceLastDomainDecomp += temp[n];
#ifdef COUNT_ACTIVE_PARTICLES
long long NumActivePatricles;
NumActivePatricles=0;
for(n = 0; n < NTask; n++)
NumActivePatricles+=temp[n];
#endif
free(temp);
t1 = second();
All.CPU_Predict += timediff(t0, t1);
while(min_glob >= All.Ti_nextoutput && All.Ti_nextoutput >= 0)
{
move_particles(All.Ti_Current, All.Ti_nextoutput);
All.Ti_Current = All.Ti_nextoutput;
if(All.ComovingIntegrationOn)
All.Time = All.TimeBegin * exp(All.Ti_Current * All.Timebase_interval);
else
All.Time = All.TimeBegin + All.Ti_Current * All.Timebase_interval;
#ifdef OUTPUTPOTENTIAL
All.NumForcesSinceLastDomainDecomp = 1 + All.TotNumPart * All.TreeDomainUpdateFrequency;
domain_Decomposition();
compute_potential();
#endif
#ifndef OUTPUT_EVERY_TIMESTEP
savepositions(All.SnapshotFileCount++); /* write snapshot file */
#endif
All.Ti_nextoutput = find_next_outputtime(All.Ti_nextoutput + 1);
}
move_particles(All.Ti_Current, min_glob);
All.Ti_Current = min_glob;
if(All.ComovingIntegrationOn)
All.Time = All.TimeBegin * exp(All.Ti_Current * All.Timebase_interval);
else
All.Time = All.TimeBegin + All.Ti_Current * All.Timebase_interval;
All.TimeStep = All.Time - timeold;
#ifdef COUNT_ACTIVE_PARTICLES
if (ThisTask==0)
{
- fprintf(FdLog,"===========================================================================================\n");
- fprintf(FdLog,"Step = %06d Time = %g \n\n",All.NumCurrentTiStep,All.Time);
+ fprintf(FdTimings,"===========================================================================================\n");
+ fprintf(FdTimings,"Step = %06d Time = %g \n\n",All.NumCurrentTiStep,All.Time);
- fprintf(FdLog,"%g %g : Total number of active particles : %d%09d\n\n",All.Time,All.TimeStep,(int) (NumActivePatricles / 1000000000), (int) (NumActivePatricles % 1000000000));
+ fprintf(FdTimings,"%g %g : Total number of active particles : %d%09d\n\n",All.Time,All.TimeStep,(int) (NumActivePatricles / 1000000000), (int) (NumActivePatricles % 1000000000));
}
int *numpartlist;
int i;
int tot;
numpartlist = malloc(sizeof(int) * NTask*6);
MPI_Gather(&N_gas, 1, MPI_INT, &numpartlist[NTask*0], 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Gather(&N_stars, 1, MPI_INT, &numpartlist[NTask*1], 1, MPI_INT, 0, MPI_COMM_WORLD);
MPI_Gather(&NumPart, 1, MPI_INT, &numpartlist[NTask*2], 1, MPI_INT, 0, MPI_COMM_WORLD);
if (ThisTask==0)
{
tot = 0;
- fprintf(FdLog,"gas ");
+ fprintf(FdTimings,"gas ");
for (i=0;i<NTask;i++)
{
- fprintf(FdLog, "%12d ",numpartlist[NTask*0+i]); /* nombre de part par proc */
+ fprintf(FdTimings, "%12d ",numpartlist[NTask*0+i]); /* nombre de part par proc */
tot += numpartlist[NTask*0+i];
}
- fprintf(FdLog, " : %12d ",tot);
- fprintf(FdLog,"\n");
+ fprintf(FdTimings, " : %12d ",tot);
+ fprintf(FdTimings,"\n");
tot = 0;
- fprintf(FdLog,"stars ");
+ fprintf(FdTimings,"stars ");
for (i=0;i<NTask;i++)
{
- fprintf(FdLog, "%12d ",numpartlist[NTask*1+i]); /* nombre de part par proc */
+ fprintf(FdTimings, "%12d ",numpartlist[NTask*1+i]); /* nombre de part par proc */
tot += numpartlist[NTask*1+i];
}
- fprintf(FdLog, " : %12d ",tot);
- fprintf(FdLog,"\n");
+ fprintf(FdTimings, " : %12d ",tot);
+ fprintf(FdTimings,"\n");
tot = 0;
- fprintf(FdLog,"remaining ");
+ fprintf(FdTimings,"remaining ");
for (i=0;i<NTask;i++)
{
- fprintf(FdLog, "%12d ",numpartlist[NTask*2+i]-numpartlist[NTask*1+i]-numpartlist[NTask*0+i]); /* nombre de part par proc */
+ fprintf(FdTimings, "%12d ",numpartlist[NTask*2+i]-numpartlist[NTask*1+i]-numpartlist[NTask*0+i]); /* nombre de part par proc */
tot += numpartlist[NTask*2+i]-numpartlist[NTask*1+i]-numpartlist[NTask*0+i];
}
- fprintf(FdLog, " : %12d ",tot);
- fprintf(FdLog,"\n\n");
+ fprintf(FdTimings, " : %12d ",tot);
+ fprintf(FdTimings,"\n\n");
tot = 0;
- fprintf(FdLog,"total ");
+ fprintf(FdTimings,"total ");
for (i=0;i<NTask;i++)
{
- fprintf(FdLog, "%12d ",numpartlist[NTask*2+i]); /* nombre de part par proc */
+ fprintf(FdTimings, "%12d ",numpartlist[NTask*2+i]); /* nombre de part par proc */
tot += numpartlist[NTask*2+i];
}
- fprintf(FdLog, " : %12d ",tot);
- fprintf(FdLog,"\n\n");
+ fprintf(FdTimings, " : %12d ",tot);
+ fprintf(FdTimings,"\n\n");
- fflush(FdLog);
+ fflush(FdTimings);
}
free(numpartlist);
#endif
#ifdef DETAILED_CPU
tend = second();
All.CPU_Leapfrog += timediff(tstart, tend);
#endif
}
/*! this function returns the next output time that is equal or larger to
* ti_curr
*/
int find_next_outputtime(int ti_curr)
{
int i, ti, ti_next, iter = 0;
double next, time;
ti_next = -1;
if(All.OutputListOn)
{
for(i = 0; i < All.OutputListLength; i++)
{
time = All.OutputListTimes[i];
if(time >= All.TimeBegin && time <= All.TimeMax)
{
if(All.ComovingIntegrationOn)
ti = log(time / All.TimeBegin) / All.Timebase_interval;
else
ti = (time - All.TimeBegin) / All.Timebase_interval;
if(ti >= ti_curr)
{
if(ti_next == -1)
ti_next = ti;
if(ti_next > ti)
ti_next = ti;
}
}
}
}
else
{
if(All.ComovingIntegrationOn)
{
if(All.TimeBetSnapshot <= 1.0)
{
printf("TimeBetSnapshot > 1.0 required for your simulation.\n");
endrun(13123);
}
}
else
{
if(All.TimeBetSnapshot <= 0.0)
{
printf("TimeBetSnapshot > 0.0 required for your simulation.\n");
endrun(13123);
}
}
time = All.TimeOfFirstSnapshot;
iter = 0;
while(time < All.TimeBegin)
{
if(All.ComovingIntegrationOn)
time *= All.TimeBetSnapshot;
else
time += All.TimeBetSnapshot;
iter++;
if(iter > 1000000)
{
printf("Can't determine next output time.\n");
endrun(110);
}
}
while(time <= All.TimeMax)
{
if(All.ComovingIntegrationOn)
ti = log(time / All.TimeBegin) / All.Timebase_interval;
else
ti = (time - All.TimeBegin) / All.Timebase_interval;
if(ti >= ti_curr)
{
ti_next = ti;
break;
}
if(All.ComovingIntegrationOn)
time *= All.TimeBetSnapshot;
else
time += All.TimeBetSnapshot;
iter++;
if(iter > 1000000)
{
printf("Can't determine next output time.\n");
endrun(111);
}
}
}
if(ti_next == -1)
{
ti_next = 2 * TIMEBASE; /* this will prevent any further output */
if(ThisTask == 0)
printf("\nThere is no valid time for a further snapshot file.\n");
}
else
{
if(All.ComovingIntegrationOn)
next = All.TimeBegin * exp(ti_next * All.Timebase_interval);
else
next = All.TimeBegin + ti_next * All.Timebase_interval;
if(ThisTask == 0)
printf("\nSetting next time for snapshot file to Time_next= %g\n\n", next);
}
return ti_next;
}
/*! This routine writes one line for every timestep to two log-files. In
* FdInfo, we just list the timesteps that have been done, while in FdCPU the
* cumulative cpu-time consumption in various parts of the code is stored.
*/
void every_timestep_stuff(void)
{
double z;
#ifdef DETAILED_CPU
double tstart,tend;
tstart = second();
#endif
if(ThisTask == 0)
{
if(All.ComovingIntegrationOn)
{
z = 1.0 / (All.Time) - 1;
fprintf(FdInfo, "\nBegin Step %d, Time: %g, Redshift: %g, Systemstep: %g, Dloga: %g\n",
All.NumCurrentTiStep, All.Time, z, All.TimeStep,
log(All.Time) - log(All.Time - All.TimeStep));
printf("\nBegin Step %d, Time: %g, Redshift: %g, Systemstep: %g, Dloga: %g\n", All.NumCurrentTiStep,
All.Time, z, All.TimeStep, log(All.Time) - log(All.Time - All.TimeStep));
fflush(FdInfo);
}
else
{
fprintf(FdInfo, "\nBegin Step %d, Time: %g, Systemstep: %g\n", All.NumCurrentTiStep, All.Time,
All.TimeStep);
printf("\nBegin Step %d, Time: %g, Systemstep: %g\n", All.NumCurrentTiStep, All.Time, All.TimeStep);
fflush(FdInfo);
}
printf("-------------------------------------------------------------\n");
fflush(stdout);
#ifdef ADVANCEDCPUSTATISTICS
fprintf(FdCPU, "%d ", All.NumCurrentTiStep);
fprintf(FdCPU, "%g ", All.Time);
fprintf(FdCPU, "%d ", NTask);
fprintf(FdCPU,"%10.2f ",All.CPU_Total);
#ifdef DETAILED_CPU
fprintf(FdCPU,"%10.2f ",All.CPU_Leapfrog);
fprintf(FdCPU,"%10.2f ",All.CPU_Physics);
fprintf(FdCPU,"%10.2f ",All.CPU_Residual);
fprintf(FdCPU,"%10.2f ",All.CPU_Accel);
fprintf(FdCPU,"%10.2f ",All.CPU_Begrun);
#endif
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity);
fprintf(FdCPU,"%10.2f ",All.CPU_Hydro);
#ifdef COOLING
fprintf(FdCPU,"%10.2f ",All.CPU_Cooling);
#endif
#ifdef SFR
fprintf(FdCPU,"%10.2f ",All.CPU_StarFormation);
#endif
#ifdef CHIMIE
fprintf(FdCPU,"%10.2f ",All.CPU_Chimie);
#endif
#ifdef MULTIPHASE
fprintf(FdCPU,"%10.2f ",All.CPU_Sticky);
#endif
fprintf(FdCPU,"%10.2f ",All.CPU_Domain);
fprintf(FdCPU,"%10.2f ",All.CPU_Potential);
fprintf(FdCPU,"%10.2f ",All.CPU_Predict);
fprintf(FdCPU,"%10.2f ",All.CPU_TimeLine);
fprintf(FdCPU,"%10.2f ",All.CPU_Snapshot);
fprintf(FdCPU,"%10.2f ",All.CPU_TreeWalk);
fprintf(FdCPU,"%10.2f ",All.CPU_TreeConstruction);
fprintf(FdCPU,"%10.2f ",All.CPU_CommSum);
fprintf(FdCPU,"%10.2f ",All.CPU_Imbalance);
fprintf(FdCPU,"%10.2f ",All.CPU_HydCompWalk);
fprintf(FdCPU,"%10.2f ",All.CPU_HydCommSumm);
fprintf(FdCPU,"%10.2f ",All.CPU_HydImbalance);
fprintf(FdCPU,"%10.2f ",All.CPU_EnsureNgb);
fprintf(FdCPU,"%10.2f ",All.CPU_PM);
fprintf(FdCPU,"%10.2f ",All.CPU_Peano);
#ifdef DETAILED_CPU_DOMAIN
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_findExtend);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_determineTopTree);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_sumCost);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_findSplit);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_shiftSplit);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_countToGo);
fprintf(FdCPU,"%10.2f ",All.CPU_Domain_exchange);
#endif
#ifdef DETAILED_CPU_GRAVITY
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_TreeWalk1);
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_TreeWalk2);
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_CommSum1);
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_CommSum2);
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_Imbalance1);
fprintf(FdCPU,"%10.2f ",All.CPU_Gravity_Imbalance2);
#endif
fprintf(FdCPU,"\n");
fflush(FdCPU);
#else
fprintf(FdCPU, "Step %d, Time: %g, CPUs: %d\n", All.NumCurrentTiStep, All.Time, NTask);
fprintf(FdCPU,
"%10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f %10.2f\n",
All.CPU_Total, All.CPU_Gravity, All.CPU_Hydro, All.CPU_Domain, All.CPU_Potential,
All.CPU_Predict, All.CPU_TimeLine, All.CPU_Snapshot, All.CPU_TreeWalk, All.CPU_TreeConstruction,
All.CPU_CommSum, All.CPU_Imbalance, All.CPU_HydCompWalk, All.CPU_HydCommSumm,
All.CPU_HydImbalance, All.CPU_EnsureNgb, All.CPU_PM, All.CPU_Peano);
fflush(FdCPU);
#endif
}
set_random_numbers();
#ifdef DETAILED_CPU
tend = second();
All.CPU_Residual += timediff(tstart, tend);
#endif
}
/*! This routine first calls a computation of various global quantities of the
* particle distribution, and then writes some statistics about the energies
* in the various particle components to the file FdEnergy.
*/
void energy_statistics(void)
{
compute_global_quantities_of_system();
if(ThisTask == 0)
{
fprintf(FdEnergy,
"%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n",
All.Time, SysState.EnergyInt, SysState.EnergyPot, SysState.EnergyKin, SysState.EnergyIntComp[0],
SysState.EnergyPotComp[0], SysState.EnergyKinComp[0], SysState.EnergyIntComp[1],
SysState.EnergyPotComp[1], SysState.EnergyKinComp[1], SysState.EnergyIntComp[2],
SysState.EnergyPotComp[2], SysState.EnergyKinComp[2], SysState.EnergyIntComp[3],
SysState.EnergyPotComp[3], SysState.EnergyKinComp[3], SysState.EnergyIntComp[4],
SysState.EnergyPotComp[4], SysState.EnergyKinComp[4], SysState.EnergyIntComp[5],
SysState.EnergyPotComp[5], SysState.EnergyKinComp[5], SysState.MassComp[0],
SysState.MassComp[1], SysState.MassComp[2], SysState.MassComp[3], SysState.MassComp[4],
SysState.MassComp[5]);
fflush(FdEnergy);
}
}
/*! This routine first calls a computation of various global quantities of the
* particle distribution, and then writes some statistics about the energies
* in the various particle components to the file FdEnergy.
*/
#ifdef ADVANCEDSTATISTICS
void advanced_energy_statistics(void)
{
int i;
#ifdef DETAILED_CPU
double tstart,tend;
tstart = second();
#endif
compute_global_quantities_of_system();
if(ThisTask == 0)
{
/**************/
/* energy */
/**************/
/* time */
fprintf(FdEnergy,"%g ",All.Time);
/* total */
fprintf(FdEnergy,"%g %g %g ",SysState.EnergyInt, SysState.EnergyPot, SysState.EnergyKin);
#ifdef COOLING
fprintf(FdEnergy,"%g ",SysState.EnergyRadSph);
#endif
#ifdef AGN_HEATING
fprintf(FdEnergy,"%g ",SysState.EnergyAGNHeat);
#endif
#ifdef MULTIPHASE
fprintf(FdEnergy,"%g ",SysState.EnergyRadSticky);
#endif
#ifdef FEEDBACK_WIND
fprintf(FdEnergy,"%g ",SysState.EnergyFeedbackWind);
#endif
#ifdef BUBBLES
fprintf(FdEnergy,"%g ",SysState.EnergyBubbles);
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
fprintf(FdEnergy,"%g ",SysState.EnergyThermalFeedback);
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
fprintf(FdEnergy,"%g ",SysState.EnergyKineticFeedback);
#endif
/* comp */
for (i=0;i<6;i++)
{
fprintf(FdEnergy,"%g %g %g ",SysState.EnergyIntComp[i],SysState.EnergyPotComp[i], SysState.EnergyKinComp[i]);
#ifdef COOLING
fprintf(FdEnergy,"%g ",SysState.EnergyRadSphComp[i]);
#endif
#ifdef AGN_HEATING
fprintf(FdEnergy,"%g ",SysState.EnergyAGNHeatComp[i]);
#endif
#ifdef MULTIPHASE
fprintf(FdEnergy,"%g ",SysState.EnergyRadStickyComp[i]);
#endif
#ifdef FEEDBACK_WIND
fprintf(FdEnergy,"%g ",SysState.EnergyFeedbackWindComp[i]);
#endif
#ifdef BUBBLES
fprintf(FdEnergy,"%g ",SysState.EnergyBubblesComp[i]);
#endif
#ifdef CHIMIE_THERMAL_FEEDBACK
fprintf(FdEnergy,"%g ",SysState.EnergyThermalFeedbackComp[i]);
#endif
#ifdef CHIMIE_KINETIC_FEEDBACK
fprintf(FdEnergy,"%g ",SysState.EnergyKineticFeedbackComp[i]);
#endif
}
/* mass */
for (i=0;i<6;i++)
{
fprintf(FdEnergy,"%g ",SysState.MassComp[i]);
}
/* return */
fprintf(FdEnergy,"\n");
fflush(FdEnergy);
#ifdef SYSTEMSTATISTICS
/**************/
/* system */
/**************/
fprintf(FdSystem,"%g %g %g %g %g %g %g %g %g %g %g %g %g\n",
All.Time,
SysState.Momentum[0], SysState.Momentum[1], SysState.Momentum[2], SysState.Momentum[3],
SysState.AngMomentum[0], SysState.AngMomentum[1], SysState.AngMomentum[2], SysState.AngMomentum[3],
SysState.CenterOfMass[0], SysState.CenterOfMass[1], SysState.CenterOfMass[2], SysState.CenterOfMass[3]);
fflush(FdSystem);
#endif
}
#ifdef DETAILED_CPU
tend = second();
All.CPU_Residual += timediff(tstart, tend);
#endif
}
#endif
diff --git a/stars_density.c b/stars_density.c
index c7cd06d..7aca1fe 100644
--- a/stars_density.c
+++ b/stars_density.c
@@ -1,654 +1,719 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <mpi.h>
#include "allvars.h"
#include "proto.h"
/*! \file density.c
* \brief SPH density computation and smoothing length determination
*
* This file contains the "first SPH loop", where the SPH densities and
* some auxiliary quantities are computed. If the number of neighbours
* obtained falls outside the target range, the correct smoothing
* length is determined iteratively, if needed.
*/
#ifdef PERIODIC
static double boxSize, boxHalf;
#ifdef LONG_X
static double boxSize_X, boxHalf_X;
#else
#define boxSize_X boxSize
#define boxHalf_X boxHalf
#endif
#ifdef LONG_Y
static double boxSize_Y, boxHalf_Y;
#else
#define boxSize_Y boxSize
#define boxHalf_Y boxHalf
#endif
#ifdef LONG_Z
static double boxSize_Z, boxHalf_Z;
#else
#define boxSize_Z boxSize
#define boxHalf_Z boxHalf
#endif
#endif
#ifdef CHIMIE
/*! This function computes the local density for each active SPH particle,
* the number of neighbours in the current smoothing radius, and the
* divergence and curl of the velocity field. The pressure is updated as
* well. If a particle with its smoothing region is fully inside the
* local domain, it is not exported to the other processors. The function
* also detects particles that have a number of neighbours outside the
* allowed tolerance range. For these particles, the smoothing length is
* adjusted accordingly, and the density computation is executed again.
* Note that the smoothing length is not allowed to fall below the lower
* bound set by MinGasHsml.
*/
void stars_density(void)
{
long long ntot, ntotleft;
int *noffset, *nbuffer, *nsend, *nsend_local, *numlist, *ndonelist;
int i, j, n,m, ndone, npleft, maxfill, source, iter = 0;
int level, ngrp, sendTask, recvTask, place, nexport;
double tstart, tend, tstart_ngb = 0, tend_ngb = 0;
double sumt, sumcomm, timengb, sumtimengb;
double timecomp = 0, timeimbalance = 0, timecommsumm = 0, sumimbalance;
MPI_Status status;
+#ifdef DETAILED_CPU_OUTPUT_IN_STARS_DENSITY
+ double *timengblist;
+ double *timecomplist;
+ double *timecommsummlist;
+ double *timeimbalancelist;
+#endif
+
+
#ifdef PERIODIC
boxSize = All.BoxSize;
boxHalf = 0.5 * All.BoxSize;
#ifdef LONG_X
boxHalf_X = boxHalf * LONG_X;
boxSize_X = boxSize * LONG_X;
#endif
#ifdef LONG_Y
boxHalf_Y = boxHalf * LONG_Y;
boxSize_Y = boxSize * LONG_Y;
#endif
#ifdef LONG_Z
boxHalf_Z = boxHalf * LONG_Z;
boxSize_Z = boxSize * LONG_Z;
#endif
#endif
if (ThisTask==0)
printf("Start star density computation.\n");
noffset = malloc(sizeof(int) * NTask); /* offsets of bunches in common list */
nbuffer = malloc(sizeof(int) * NTask);
nsend_local = malloc(sizeof(int) * NTask);
nsend = malloc(sizeof(int) * NTask * NTask);
ndonelist = malloc(sizeof(int) * NTask);
/* `NumStUpdate' gives the number of particles on this processor that want a chimie computation */
for(n = N_gas, NumStUpdate = 0; n < N_gas+N_stars; n++)
{
m = P[n].StPIdx;
StP[m].Left = StP[m].Right = 0;
#ifdef AVOIDNUMNGBPROBLEM
StP[m].OldNumNgb = -1;
#endif
if(P[n].Ti_endstep == All.Ti_Current)
NumStUpdate++;
}
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&NumStUpdate, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
/* we will repeat the whole thing for those particles where we didn't
* find enough neighbours
*/
do
{
i = N_gas; /* beginn with the first star */
ntotleft = ntot; /* particles left for all tasks together */
while(ntotleft > 0)
{
for(j = 0; j < NTask; j++)
nsend_local[j] = 0;
/* do local particles and prepare export list */
tstart = second();
for(nexport = 0, ndone = 0; i < N_gas+N_stars && nexport < All.BunchSizeDensity - NTask; i++)
if(P[i].Ti_endstep == All.Ti_Current)
{
if(P[i].Type != ST)
{
printf("P[i].Type != ST, we better stop.\n");
printf("N_gas=%d (type=%d) i=%d (type=%d)\n",N_gas,P[N_gas].Type,i,P[i].Type);
printf("Please, check that you do not use PEANOHILBERT\n");
endrun(666001);
}
ndone++;
m = P[i].StPIdx;
for(j = 0; j < NTask; j++)
Exportflag[j] = 0;
stars_density_evaluate(i, 0);
for(j = 0; j < NTask; j++)
{
if(Exportflag[j])
{
StarsDensDataIn[nexport].Pos[0] = P[i].Pos[0];
StarsDensDataIn[nexport].Pos[1] = P[i].Pos[1];
StarsDensDataIn[nexport].Pos[2] = P[i].Pos[2];
StarsDensDataIn[nexport].Hsml = StP[m].Hsml;
StarsDensDataIn[nexport].Index = i;
StarsDensDataIn[nexport].Task = j;
nexport++;
nsend_local[j]++;
}
}
}
tend = second();
timecomp += timediff(tstart, tend);
qsort(StarsDensDataIn, nexport, sizeof(struct starsdensdata_in), stars_dens_compare_key);
for(j = 1, noffset[0] = 0; j < NTask; j++)
noffset[j] = noffset[j - 1] + nsend_local[j - 1];
tstart = second();
MPI_Allgather(nsend_local, NTask, MPI_INT, nsend, NTask, MPI_INT, MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* now do the particles that need to be exported */
for(level = 1; level < (1 << PTask); level++)
{
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeDensity)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* get the particles */
MPI_Sendrecv(&StarsDensDataIn[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct starsdensdata_in), MPI_BYTE,
recvTask, TAG_DENS_A,
&StarsDensDataGet[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct starsdensdata_in),
MPI_BYTE, recvTask, TAG_DENS_A, MPI_COMM_WORLD, &status);
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
tstart = second();
for(j = 0; j < nbuffer[ThisTask]; j++)
stars_density_evaluate(j, 1);
tend = second();
timecomp += timediff(tstart, tend);
/* do a block to explicitly measure imbalance */
tstart = second();
MPI_Barrier(MPI_COMM_WORLD);
tend = second();
timeimbalance += timediff(tstart, tend);
/* get the result */
tstart = second();
for(j = 0; j < NTask; j++)
nbuffer[j] = 0;
for(ngrp = level; ngrp < (1 << PTask); ngrp++)
{
maxfill = 0;
for(j = 0; j < NTask; j++)
{
if((j ^ ngrp) < NTask)
if(maxfill < nbuffer[j] + nsend[(j ^ ngrp) * NTask + j])
maxfill = nbuffer[j] + nsend[(j ^ ngrp) * NTask + j];
}
if(maxfill >= All.BunchSizeDensity)
break;
sendTask = ThisTask;
recvTask = ThisTask ^ ngrp;
if(recvTask < NTask)
{
if(nsend[ThisTask * NTask + recvTask] > 0 || nsend[recvTask * NTask + ThisTask] > 0)
{
/* send the results */
MPI_Sendrecv(&StarsDensDataResult[nbuffer[ThisTask]],
nsend[recvTask * NTask + ThisTask] * sizeof(struct starsdensdata_out),
MPI_BYTE, recvTask, TAG_DENS_B,
&StarsDensDataPartialResult[noffset[recvTask]],
nsend_local[recvTask] * sizeof(struct starsdensdata_out),
MPI_BYTE, recvTask, TAG_DENS_B, MPI_COMM_WORLD, &status);
/* add the result to the particles */
for(j = 0; j < nsend_local[recvTask]; j++)
{
source = j + noffset[recvTask];
place = P[StarsDensDataIn[source].Index].StPIdx;
StP[place].NumNgb += StarsDensDataPartialResult[source].Ngb;
StP[place].Density += StarsDensDataPartialResult[source].Rho;
StP[place].Volume += StarsDensDataPartialResult[source].Volume;
#ifdef CHIMIE_KINETIC_FEEDBACK
StP[place].NgbMass += StarsDensDataPartialResult[source].NgbMass;
#endif
StP[place].DhsmlDensityFactor += StarsDensDataPartialResult[source].DhsmlDensity;
}
}
}
for(j = 0; j < NTask; j++)
if((j ^ ngrp) < NTask)
nbuffer[j] += nsend[(j ^ ngrp) * NTask + j];
}
tend = second();
timecommsumm += timediff(tstart, tend);
level = ngrp - 1;
}
MPI_Allgather(&ndone, 1, MPI_INT, ndonelist, 1, MPI_INT, MPI_COMM_WORLD);
for(j = 0; j < NTask; j++)
ntotleft -= ndonelist[j];
}
/* do final operations on results */
tstart = second();
for(i = N_gas, npleft = 0; i < N_gas+N_stars; i++)
{
if((P[i].Ti_endstep == All.Ti_Current))
{
m = P[i].StPIdx;
StP[m].DhsmlDensityFactor =
1 / (1 + StP[m].Hsml * StP[m].DhsmlDensityFactor / (NUMDIMS * StP[m].Density));
/* now check whether we had enough neighbours */
if(StP[m].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation) ||
(StP[m].NumNgb > (All.DesNumNgb + All.MaxNumNgbDeviation)
&& StP[m].Hsml > (1.01 * All.MinGasHsml)))
{
#ifdef AVOIDNUMNGBPROBLEM
// if((StP[m].NumNgb>StP[m].OldNumNgb-All.MaxNumNgbDeviation/10000.)
// &&(StP[m].NumNgb<StP[m].OldNumNgb+All.MaxNumNgbDeviation/10000.))
if(StP[m].NumNgb==StP[m].OldNumNgb)
{
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
printf("ID=%d NumNgb=%g OldNumNgb=%g\n",P[i].ID,StP[m].NumNgb,StP[m].OldNumNgb);
continue;
}
StP[m].OldNumNgb = StP[m].NumNgb;
#endif
/* need to redo this particle */
npleft++;
if(StP[m].Left > 0 && StP[m].Right > 0)
if((StP[m].Right - StP[m].Left) < 1.0e-3 * StP[m].Left)
{
/* this one should be ok */
npleft--;
P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */
continue;
}
if(StP[m].NumNgb < (All.DesNumNgb - All.MaxNumNgbDeviation))
StP[m].Left = dmax(StP[m].Hsml, StP[m].Left);
else
{
if(StP[m].Right != 0)
{
if(StP[m].Hsml < StP[m].Right)
StP[m].Right = StP[m].Hsml;
}
else
StP[m].Right = StP[m].Hsml;
}
if(iter >= MAXITER - 10)
{
printf
("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n",
i, ThisTask, (int) P[i].ID, StP[m].Hsml, StP[m].Left, StP[m].Right,
(float) StP[m].NumNgb, StP[m].Right - StP[m].Left, P[i].Pos[0], P[i].Pos[1],
P[i].Pos[2]);
fflush(stdout);
}
if(StP[m].Right > 0 && StP[m].Left > 0)
StP[m].Hsml = pow(0.5 * (pow(StP[m].Left, 3) + pow(StP[m].Right, 3)), 1.0 / 3);
else
{
if(StP[m].Right == 0 && StP[m].Left == 0)
{
printf
("i=%d task=%d ID=%d Hsml=%g Left=%g Right=%g Ngbs=%g Right-Left=%g\n pos=(%g|%g|%g)\n",
i, ThisTask, (int) P[i].ID, StP[m].Hsml, StP[m].Left, StP[m].Right,
(float) StP[m].NumNgb, StP[m].Right - StP[m].Left, P[i].Pos[0], P[i].Pos[1],
P[i].Pos[2]);
fflush(stdout);
endrun(8188); /* can't occur */
}
if(StP[m].Right == 0 && StP[m].Left > 0)
{
if(P[i].Type == ST && fabs(StP[m].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb)
{
StP[m].Hsml *=
1 - (StP[m].NumNgb -
All.DesNumNgb) / (NUMDIMS * StP[m].NumNgb) * StP[m].DhsmlDensityFactor;
}
else
StP[m].Hsml *= 1.26;
}
if(StP[m].Right > 0 && StP[m].Left == 0)
{
if(P[i].Type == ST && fabs(StP[m].NumNgb - All.DesNumNgb) < 0.5 * All.DesNumNgb)
{
StP[m].Hsml *=
1 - (StP[m].NumNgb -
All.DesNumNgb) / (NUMDIMS * StP[m].NumNgb) * StP[m].DhsmlDensityFactor;
}
else
StP[m].Hsml /= 1.26;
}
}
if(StP[m].Hsml < All.MinGasHsml)
StP[m].Hsml = All.MinGasHsml;
}
else
P[i].Ti_endstep = -P[i].Ti_endstep - 1; /* Mark as inactive */
}
}
tend = second();
timecomp += timediff(tstart, tend);
numlist = malloc(NTask * sizeof(int) * NTask);
MPI_Allgather(&npleft, 1, MPI_INT, numlist, 1, MPI_INT, MPI_COMM_WORLD);
for(i = 0, ntot = 0; i < NTask; i++)
ntot += numlist[i];
free(numlist);
if(ntot > 0)
{
if(iter == 0)
tstart_ngb = second();
iter++;
if(iter > 0 && ThisTask == 0)
{
printf("ngb iteration %d: need to repeat for %d%09d particles.\n", iter,
(int) (ntot / 1000000000), (int) (ntot % 1000000000));
fflush(stdout);
}
if(iter > MAXITER)
{
printf("failed to converge in neighbour iteration in density()\n");
fflush(stdout);
endrun(1155);
}
}
else
tend_ngb = second();
}
while(ntot > 0);
/* mark as active again */
for(i = 0; i < NumPart; i++)
if(P[i].Ti_endstep < 0)
P[i].Ti_endstep = -P[i].Ti_endstep - 1;
free(ndonelist);
free(nsend);
free(nsend_local);
free(nbuffer);
free(noffset);
/* collect some timing information */
if(iter > 0)
timengb = timediff(tstart_ngb, tend_ngb);
else
timengb = 0;
MPI_Reduce(&timengb, &sumtimengb, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecomp, &sumt, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timecommsumm, &sumcomm, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
MPI_Reduce(&timeimbalance, &sumimbalance, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
+
+#ifdef DETAILED_CPU_OUTPUT_IN_STARS_DENSITY
+
+ numlist = malloc(sizeof(int) * NTask);
+ timengblist = malloc(sizeof(double) * NTask);
+ timecomplist = malloc(sizeof(double) * NTask);
+ timecommsummlist = malloc(sizeof(double) * NTask);
+ timeimbalancelist = malloc(sizeof(double) * NTask);
+
+ MPI_Gather(&NumStUpdate, 1, MPI_INT, numlist, 1, MPI_INT, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timengb, 1, MPI_DOUBLE, timengblist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecomp, 1, MPI_DOUBLE, timecomplist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timecommsumm, 1, MPI_DOUBLE, timecommsummlist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+ MPI_Gather(&timeimbalance, 1, MPI_DOUBLE, timeimbalancelist, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
+
if(ThisTask == 0)
{
- All.CPU_HydCompWalk += sumt / NTask;
- All.CPU_HydCommSumm += sumcomm / NTask;
- All.CPU_HydImbalance += sumimbalance / NTask;
- All.CPU_EnsureNgb += sumtimengb / NTask;
+ fprintf(FdTimings, "\n stars density \n\n");
+
+ fprintf(FdTimings, "Nupdate ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12d ",numlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timengb ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timengblist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecomp ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecomplist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timecommsumm ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timecommsummlist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "timeimbalance ");
+ for (i=0;i<NTask;i++)
+ fprintf(FdTimings, "%12g ",timeimbalancelist[i]);
+ fprintf(FdTimings, "\n");
+
+ fprintf(FdTimings, "\n");
+ }
+
+ free(timeimbalancelist);
+ free(timecommsummlist);
+ free(timecomplist);
+ free(timengblist);
+ free(numlist);
+
+
+#endif
+
+
+ if(ThisTask == 0)
+ {
+ All.CPU_ChimieDensCompWalk += sumt / NTask;
+ All.CPU_ChimieDensCommSumm += sumcomm / NTask;
+ All.CPU_ChimieDensImbalance += sumimbalance / NTask;
+ All.CPU_ChimieDensEnsureNgb += sumtimengb / NTask;
}
if (ThisTask==0)
printf("Stars density computation done.\n");
}
/*! This function represents the core of the SPH density computation. The
* target particle may either be local, or reside in the communication
* buffer.
*/
void stars_density_evaluate(int target, int mode)
{
int j, n, startnode, numngb, numngb_inbox;
double h, h2, hinv, hinv3, hinv4;
double rho, volume, wk, dwk;
double dx, dy, dz, r, r2, u, mass_j,rho_j;
double weighted_numngb, dhsmlrho;
int target_stp;
FLOAT *pos;
#ifdef CHIMIE_KINETIC_FEEDBACK
double ngbmass;
#endif
if(mode == 0)
{
pos = P[target].Pos;
target_stp = P[target].StPIdx;
h = StP[target_stp].Hsml;
}
else
{
pos = StarsDensDataGet[target].Pos;
h = StarsDensDataGet[target].Hsml;
}
h2 = h * h;
hinv = 1.0 / h;
#ifndef TWODIMS
hinv3 = hinv * hinv * hinv;
#else
hinv3 = hinv * hinv / boxSize_Z;
#endif
hinv4 = hinv3 * hinv;
rho = 0;
volume = 0;
weighted_numngb = 0;
dhsmlrho = 0;
#ifdef CHIMIE_KINETIC_FEEDBACK
ngbmass=0;
#endif
startnode = All.MaxPart;
numngb = 0;
do
{
numngb_inbox = ngb_treefind_variable_for_chimie(&pos[0], h, &startnode);
for(n = 0; n < numngb_inbox; n++)
{
j = Ngblist[n];
dx = pos[0] - P[j].Pos[0];
dy = pos[1] - P[j].Pos[1];
dz = pos[2] - P[j].Pos[2];
#ifdef PERIODIC /* now find the closest image in the given box size */
if(dx > boxHalf_X)
dx -= boxSize_X;
if(dx < -boxHalf_X)
dx += boxSize_X;
if(dy > boxHalf_Y)
dy -= boxSize_Y;
if(dy < -boxHalf_Y)
dy += boxSize_Y;
if(dz > boxHalf_Z)
dz -= boxSize_Z;
if(dz < -boxHalf_Z)
dz += boxSize_Z;
#endif
r2 = dx * dx + dy * dy + dz * dz;
if(r2 < h2)
{
numngb++;
r = sqrt(r2);
u = r * hinv;
if(u < 0.5)
{
wk = hinv3 * (KERNEL_COEFF_1 + KERNEL_COEFF_2 * (u - 1) * u * u);
dwk = hinv4 * u * (KERNEL_COEFF_3 * u - KERNEL_COEFF_4);
}
else
{
wk = hinv3 * KERNEL_COEFF_5 * (1.0 - u) * (1.0 - u) * (1.0 - u);
dwk = hinv4 * KERNEL_COEFF_6 * (1.0 - u) * (1.0 - u);
}
mass_j = P[j].Mass;
rho_j = SphP[j].Density;
rho += mass_j * wk;
volume += mass_j/rho_j * wk;
weighted_numngb += NORM_COEFF * wk / hinv3;
dhsmlrho += -mass_j * (NUMDIMS * hinv * wk + u * dwk);
#ifdef CHIMIE_KINETIC_FEEDBACK
ngbmass += mass_j;
#endif
}
}
}
while(startnode >= 0);
if(mode == 0)
{
StP[target_stp].NumNgb = weighted_numngb;
StP[target_stp].Density = rho;
StP[target_stp].Volume = volume;
StP[target_stp].DhsmlDensityFactor = dhsmlrho;
#ifdef CHIMIE_KINETIC_FEEDBACK
StP[target_stp].NgbMass = ngbmass;
#endif
}
else
{
StarsDensDataResult[target].Rho = rho;
StarsDensDataResult[target].Volume = volume;
StarsDensDataResult[target].Ngb = weighted_numngb;
StarsDensDataResult[target].DhsmlDensity = dhsmlrho;
#ifdef CHIMIE_KINETIC_FEEDBACK
StarsDensDataResult[target].NgbMass = ngbmass;
#endif
}
}
/*! This routine is a comparison kernel used in a sort routine to group
* particles that are exported to the same processor.
*/
int stars_dens_compare_key(const void *a, const void *b)
{
if(((struct starsdensdata_in *) a)->Task < (((struct starsdensdata_in *) b)->Task))
return -1;
if(((struct starsdensdata_in *) a)->Task > (((struct starsdensdata_in *) b)->Task))
return +1;
return 0;
}
#endif

Event Timeline