diff --git a/.gitignore b/.gitignore index 1c8c4b5f..4376b0ba 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ *.out *.app +# Jupiter files +*.ipynb_checkpoints + # Other Scripts/symbolic_hermitians/*.pyc Scripts/symbolic_hermitians/__pycache__ @@ -39,4 +42,5 @@ Scripts/symbolic_hermitians/__pycache__ .pydevproject .settings .vscode -Source/generated_files \ No newline at end of file +Source/generated_files +*~ diff --git a/.gitmodules b/.gitmodules index c5213734..5d5d624a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "amrex"] - path = amrex - url = https://github.com/dwillcox/amrex.git + path = submodules/amrex + url = https://github.com/AMReX-Codes/amrex.git +[submodule "submodules/HighFive"] + path = submodules/HighFive + url = https://github.com/BlueBrain/HighFive.git diff --git a/.gitpod.dockerfile b/.gitpod.dockerfile deleted file mode 100644 index 4ae39609..00000000 --- a/.gitpod.dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM gitpod/workspace-full - -RUN sudo apt-get update && sudo apt-get install -y gfortran libopenmpi3 libopenmpi-dev && sudo rm -rf /var/lib/apt/lists/* - diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 05d8da7b..00000000 --- a/.gitpod.yml +++ /dev/null @@ -1,98 +0,0 @@ -image: - file: .gitpod.dockerfile - -tasks: - - init: |- - sudo apt -y install gfortran libopenmpi3 libopenmpi-dev - mkdir /workspace/python - python -m venv /workspace/python/emu --system-site-packages - source /workspace/python/emu/bin/activate - pip install sympy numpy matplotlib - - pushd Scripts/visualization - python setup.py install - popd - - cd Exec - - make generate NUM_FLAVORS=2 - make -j NUM_FLAVORS=2 - mkdir 2-Flavors - mv *.ex 2-Flavors/. - pushd 2-Flavors - - mkdir bipolar - pushd bipolar - cp ../*.ex . - cp ../../../sample_inputs/inputs_bipolar_test . - cp ../../../Scripts/visualization/plot_first_particle.py . - popd - - mkdir msw - pushd msw - cp ../*.ex . - cp ../../../sample_inputs/inputs_msw_test . - cp ../../../Scripts/visualization/plot_first_particle.py . - popd - - mkdir FFI_zero_wavenumber - pushd FFI_zero_wavenumber - cp ../*.ex . - cp ../../../sample_inputs/inputs_fast_flavor . - popd - - mkdir FFI - pushd FFI - cp ../*.ex . - cp ../../../sample_inputs/inputs_fast_flavor_nonzerok . - popd - - popd - - make realclean - make generate NUM_FLAVORS=3 - make -j NUM_FLAVORS=3 - mkdir 3-Flavors - mv *.ex 3-Flavors/. - pushd 3-Flavors - - mkdir FFI - pushd FFI - cp ../*.ex . - cp ../../../sample_inputs/inputs_1d_fiducial . - popd - - popd - - make realclean - - mkdir Examples - mv 2-Flavors Examples/. - mv 3-Flavors Examples/. - mv Examples ../. - - clear - - - command: |- - source /workspace/python/emu/bin/activate - clear - -github: - prebuilds: - # enable for the master/default branch (defaults to true) - master: true - # enable for all branches in this repo (defaults to false) - branches: false - # enable for pull requests coming from this repo (defaults to true) - pullRequests: false - # enable for pull requests coming from forks (defaults to false) - pullRequestsFromForks: false - # add a check to pull requests (defaults to true) - addCheck: true - # add a "Review in Gitpod" button as a comment to pull requests (defaults to false) - addComment: true - # add a "Review in Gitpod" button to the pull request's description (defaults to false) - addBadge: true - # add a label once the prebuild is ready to pull requests (defaults to false) - addLabel: false - diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bd44f824..00000000 --- a/.travis.yml +++ /dev/null @@ -1,36 +0,0 @@ -dist: xenial - -language: python - -python: - - "3.7-dev" - -before_install: - - export PATH=$(echo $PATH | tr ':' "\n" | sed '/\/opt\/python/d' | tr "\n" ":" | sed "s|::|:|g") - - pip install sympy numpy - -addons: - apt: - packages: - - mpich - - libmpich-dev - - g++ - - gfortran - - libgmp-dev - - libmpfr-dev - - pandoc - - doxygen - - texlive - - texlive-latex-extra - - texlive-lang-cjk - - latexmk - -install: -- git submodule init; git submodule update -- cp makefiles/GNUmakefile_travis Exec/GNUmakefile; cd Exec; make - -script: -- cd Exec; mpirun -np 2 ./main3d.gnu.DEBUG.TPROF.MPI.ex ../sample_inputs/inputs_msw_test; python ../Scripts/tests/msw_test.py -- cd Exec; mpirun -np 2 ./main3d.gnu.DEBUG.TPROF.MPI.ex ../sample_inputs/inputs_bipolar_test -- cd Exec; mpirun -np 2 ./main3d.gnu.DEBUG.TPROF.MPI.ex ../sample_inputs/inputs_fast_flavor; python ../Scripts/tests/fast_flavor_test.py -- cd Exec; mpirun -np 2 ./main3d.gnu.DEBUG.TPROF.MPI.ex ../sample_inputs/inputs_fast_flavor_nonzerok; python ../Scripts/tests/fast_flavor_k_test.py diff --git a/CHANGES.md b/CHANGES.md index 6017bdfb..bf3777e0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,16 @@ +# 1.4 + + * Restructured initial conditions to have python scripts generate particle distributions instead of doing so inside the C++ code. This improves the code's flexibility. + + * Add option to output data in HDF5 format. Post-processing scripts only work with the original binary format, since yt only reads the binary format. + + * Add realtime output of scalar quantities to make basic analysis many times faster than with the post-processing scripts. + + * Include all of the basic post-processing scripts with Emu itself to avoid keeping multiple incompatible copies of them. + +# 1.3 + + * Incorporated various feature additions used for _Code Comparison for Fast Flavor Instability Simulations_ (https://doi.org/10.1103/PhysRevD.106.043011) # 1.2 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..5447ec41 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,6 @@ +FROM nvidia/cuda:11.4.3-devel-ubuntu20.04 +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y python3 python3-pip gfortran build-essential libhdf5-openmpi-dev openmpi-bin pkg-config libopenmpi-dev openmpi-bin libblas-dev liblapack-dev libpnetcdf-dev git python-is-python3 gnuplot +RUN pip3 install numpy matplotlib h5py scipy sympy yt +ENV USER=jenkins +ENV LOGNAME=jenkins diff --git a/Emu_Fixes.md b/Emu_Fixes.md new file mode 100644 index 00000000..e69de29b diff --git a/Exec/.gitignore b/Exec/.gitignore index e02b640b..7797b25a 100644 --- a/Exec/.gitignore +++ b/Exec/.gitignore @@ -5,3 +5,6 @@ plt* tmp_build_dir Backtrace.* *.png +*.dat +*.h5 +*.pdf diff --git a/Exec/GNUmakefile b/Exec/GNUmakefile deleted file mode 100644 index faffd2b4..00000000 --- a/Exec/GNUmakefile +++ /dev/null @@ -1,32 +0,0 @@ -EMU_HOME ?= ../ -AMREX_HOME ?= ../amrex - -SHAPE_FACTOR_ORDER ?= 2 -NUM_FLAVORS ?= 3 - -COMP = gnu - -DEBUG = FALSE - -USE_MPI = TRUE -USE_OMP = FALSE -USE_ACC = FALSE -USE_CUDA = FALSE -USE_HDF5 = FALSE - -TINY_PROFILE = TRUE -USE_PARTICLES = TRUE - -PRECISION = DOUBLE - -Bpack := -Blocs := . - -ifeq ($(USE_HDF5), TRUE) -HDF5_HOME = -DEFINES += -DAMREX_USE_HDF5 -INCLUDE_LOCATIONS += $(HDF5_HOME)/include -LIBRARIES += -L$(HDF5_HOME)/lib -lhdf5 -lz -ldl -endif - -include $(EMU_HOME)/Make.Emu diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..a65e51f6 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,154 @@ +pipeline { + triggers { pollSCM('') } // Run tests whenever a new commit is detected. + agent { dockerfile {args '--gpus all -v /mnt/scratch/EOS:/EOS:ro -v /mnt/scratch/NuLib:/NuLib:ro'}} // Use the Dockerfile defined in the root Flash-X directory + environment { + // Get rid of Read -1, expected , errno =1 error + // See https://github.com/open-mpi/ompi/issues/4948 + OMPI_MCA_btl_vader_single_copy_mechanism = 'none' + } + stages { + + //=============================// + // Set up submodules and amrex // + //=============================// + stage('Prerequisites'){ steps{ + sh 'mpicc -v' + sh 'nvidia-smi' + sh 'nvcc -V' + sh 'git submodule update --init' + sh 'cp makefiles/GNUmakefile_jenkins_HDF5_CUDA Exec/GNUmakefile' + dir('Exec'){ + sh 'make generate; make -j' + } + } + } + + stage('MSW'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st0_msw_test.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_msw_test' + sh 'python ../Scripts/tests/msw_test.py' + sh 'rm -rf plt*' + } + } + } + + stage('Bipolar'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st1_bipolar_test.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_bipolar_test' + sh 'rm -rf plt*' + } + } + } + + stage('Fast Flavor'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st2_2beam_fast_flavor.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_fast_flavor' + sh 'python ../Scripts/tests/fast_flavor_test.py' + sh 'rm -rf plt*' + } + } + } + + stage('Fast Flavor k'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st3_2beam_fast_flavor_nonzerok.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_fast_flavor_nonzerok' + sh 'python ../Scripts/tests/fast_flavor_k_test.py' + sh 'rm -rf plt*' + } + } + } + + stage('Fiducial 2F GPU Binary'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st4_linear_moment_ffi.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_1d_fiducial' + sh 'python ../Scripts/data_reduction/reduce_data_fft.py' + sh 'python ../Scripts/data_reduction/reduce_data.py' + sh 'python ../Scripts/data_reduction/combine_files.py plt _reduced_data.h5' + sh 'python ../Scripts/data_reduction/combine_files.py plt _reduced_data_fft_power.h5' + sh 'python ../Scripts/babysitting/avgfee.py' + sh 'python ../Scripts/babysitting/power_spectrum.py' + sh 'python ../Scripts/data_reduction/convertToHDF5.py' + sh 'gnuplot ../Scripts/babysitting/avgfee_gnuplot.plt' + archiveArtifacts artifacts: '*.pdf' + sh 'rm -rf plt*' + } + } + } + + stage('Fiducial 3F CPU HDF5'){ steps{ + dir('Exec'){ + sh 'cp ../makefiles/GNUmakefile_jenkins_HDF5 GNUmakefile' + sh 'make realclean; make generate; make -j' + sh 'python ../Scripts/initial_conditions/st4_linear_moment_ffi_3F.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.ex ../sample_inputs/inputs_1d_fiducial' + /*sh 'python3 ../Scripts/babysitting/avgfee_HDF5.py'*/ + sh 'rm -rf plt*' + } + } + } + + stage('Collisions flavor instability'){ steps{ + dir('Exec'){ + sh 'cp ../makefiles/GNUmakefile_jenkins_HDF5_CUDA GNUmakefile' + sh 'make realclean; make generate NUM_FLAVORS=2; make -j NUM_FLAVORS=2' + sh 'python ../Scripts/initial_conditions/st8_coll_inst_test.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_collisional_instability_test' + sh 'python ../Scripts/data_reduction/reduce_data.py' + sh 'python ../Scripts/tests/coll_inst_test.py' + sh 'rm -rf plt* *pdf' + } + } + } + stage('BC periodic empty'){ steps{ + dir('Exec'){ + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_bc_periodic_init' + sh 'python ../Scripts/collisions/writeparticleinfohdf5.py' + sh 'python ../Scripts/tests/bc_empty_init_test.py' + sh 'rm -rf plt* *pdf' + } + } + } + stage('Collisions to equilibrium'){ steps{ + dir('Exec'){ + sh 'cp ../makefiles/GNUmakefile_jenkins_HDF5_CUDA GNUmakefile' + sh 'make realclean; make generate NUM_FLAVORS=3; make -j NUM_FLAVORS=3' + sh 'python ../Scripts/initial_conditions/st7_empty_particles.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_coll_equi_test' + sh 'python ../Scripts/tests/coll_equi_test.py' + sh 'rm -rf plt* *pdf' + } + } + } + + stage('Fermi-Dirac test'){ steps{ + dir('Exec'){ + sh 'python ../Scripts/initial_conditions/st9_empty_particles_multi_energy.py' + sh 'python ../Scripts/collisions/nsm_constant_background_rho_Ye_T__writer.py' + sh 'mpirun -np 4 ./main3d.gnu.TPROF.MPI.CUDA.ex ../sample_inputs/inputs_fermi_dirac_test' + sh 'python ../Scripts/collisions/writeparticleinfohdf5.py' + sh 'python ../Scripts/tests/fermi_dirac_test.py' + sh 'rm -rf plt* *pdf rho_Ye_T.hdf5' + } + } + } + + } // stages{ + + post { + always { + cleanWs( + cleanWhenNotBuilt: true, + deleteDirs: true, + disableDeferredWipeout: false, + notFailBuild: true, + patterns: [[pattern: 'submodules', type: 'EXCLUDE']] + ) // allow submodules to be cached + } + } + +} // pipeline{ diff --git a/Make.Emu b/Make.Emu index 24d9d145..056f7940 100644 --- a/Make.Emu +++ b/Make.Emu @@ -1,11 +1,18 @@ -NUM_FLAVORS ?= 2 -SHAPE_FACTOR_ORDER ?= 2 +# things that used to be defined in the makefile in Exec DIM = 3 +TINY_PROFILE = TRUE +USE_PARTICLES = TRUE +PRECISION = DOUBLE +Bpack := +Blocs := . + TOP := $(EMU_HOME) EBASE := main +CXXSTD := c++17 + include $(AMREX_HOME)/Tools/GNUMake/Make.defs Bdirs := Source @@ -22,13 +29,13 @@ Ppack += $(foreach dir, $(Pdirs), $(AMREX_HOME)/Src/$(dir)/Make.pack include $(Ppack) -DEFINES += -DNUM_FLAVORS=$(NUM_FLAVORS) -DSHAPE_FACTOR_ORDER=$(SHAPE_FACTOR_ORDER) +DEFINES += -DNUM_FLAVORS=$(NUM_FLAVORS) -DSHAPE_FACTOR_ORDER=$(SHAPE_FACTOR_ORDER) -DNUM_MOMENTS=$(NUM_MOMENTS) all: generate $(objEXETempDir)/AMReX_buildInfo.o $(executable) @echo SUCCESS generate: - python3 $(EMU_HOME)/Scripts/symbolic_hermitians/generate_code.py $(NUM_FLAVORS) --emu_home $(EMU_HOME) + python3 $(EMU_HOME)/Scripts/symbolic_hermitians/generate_code.py $(NUM_FLAVORS) --emu_home $(EMU_HOME) --num_moments $(NUM_MOMENTS) #------------------------------------------------------------------------------ # build info (from Castro/Exec/Make.auto_source) diff --git a/README.md b/README.md index f7591bbc..ecf2545e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ [![DOI](https://zenodo.org/badge/228717670.svg)](https://zenodo.org/badge/latestdoi/228717670) [![AMReX](https://amrex-codes.github.io/badges/powered%20by-AMReX-red.svg)](https://amrex-codes.github.io) -[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/AMReX-Astro/Emu) - ![Emu](https://github.com/AMReX-Astro/Emu/blob/development/Docs/Emu_logo_transparent.png) # Emu @@ -25,53 +23,81 @@ Emu is implemented in C++ and is based on the AMReX library for high-performance, block-structured adaptive mesh refinement. Emu is parallelized with MPI + OpenMP for CPUs and MPI + CUDA for GPUs. -# Try Emu in Your Browser! +# Getting Started From Scratch +If you would like to run Emu on your own machine, there are some packages +you will need to install depending on your operating system. + +## Linux or WSL -To quickly try Emu out using your browser, you can -[open an interactive Emu workspace in Gitpod!](https://gitpod.io/#https://github.com/AMReX-Astro/Emu) +If you are running Emu on Linux or a WSL, you will need to install the +following packages: + +``` +apt-get install g++ libopenmpi-dev python-3 gfortran gnuplot-x11 +pip install sympy h5py +``` -Emu's prebuilt Gitpod workspace tracks the current release branch, and you can find pre-compiled examples in the `Examples` directory. +## MacOS -For example, to run and visualize the MSW setup: +If you are running Emu on macOS you will need gcc and a mpi wrapper: ``` -cd Examples/2-Flavors/msw -./main3d.gnu.TPROF.MPI.ex inputs_msw_test -python plot_first_particle.py +brew install mpich --cc=gcc-13 ``` -And then open the plot through the file browser on the left of the screen. -# Getting Started From Scratch +## Using Emu + -If you would like to run Emu on your own machine, first clone Emu with the AMReX submodule: +After installing those modules, clone Emu with the AMReX submodule: ``` git clone --recurse-submodules https://github.com/AMReX-Astro/Emu.git +git submodule update ``` -Then change directories to `Emu/Exec`. +Then change directories to `Emu/Exec`. Before each compilation, you must symbolically generate Emu source code for +the number of neutrino flavors you wish to use and specify a few other compile-time settings in a file called `GNUmakefile`. + +Copy in a default makefile. In this file you can specify the number of neutrino flavors, whether to compile for GPUs, etc. We have set the defaults to 2 neutrino flavors, order 2 PIC shape factors, and compiling for a single CPU. + +For Linux or WSL: -Before each compilation, you must symbolically generate Emu source code for -the number of neutrino flavors you wish to use. Do this like: +``` +cp ../makefiles/GNUmakefile_default GNUmakefile +``` + +For MacOS: ``` -make generate NUM_FLAVORS=2 +cp ../makefiles/GNUmakefile_macOS GNUmakefile ``` -Then compile Emu with `make`, e.g.: + +Compiling occurs in two stages. We first have to generate code according to the number of neutrino flavors. +``` +make generate ``` -make NUM_FLAVORS=2 +Then we have to compile Emu. +``` +make -j ``` -Emu parameters are set in an input file, and we provide a series of sample -input files for various simulation setups in `Emu/sample_inputs`. +The initial particle distribution is set by an ASCII particle data file. You can generate the data file with our initial condition scripts. For instance, if we want to simulate a two-beam fast flavor instability, generate the initial conditions using +``` +python3 ../Scripts/initial_conditions/st3_2beam_fast_flavor_nonzerok.py +``` +You should now see a new file called `particle_input.dat`. -You can run the MSW setup in Emu by doing: +The parameters for the simulation are set in input files. These include information about things like the size of the domain, the number of grid cells, and fundamental neutrino properties. Run the fast flavor test simulation using the particle distribution generated previously using one of the test input files stored in `sample_inputs` +``` +./main3d.gnu.TPROF.ex ../sample_inputs/inputs_fast_flavor_nonzerok +``` +We have a number of data reduction, analysis, and visualization scripts in the `Scripts` directory. Generate a PDF file titled `avgfee.pdf` showing the time evolution of the average number density of electron neutrinos using ``` -./main3d.gnu.TPROF.MPI.ex inputs_msw_test +gnuplot ../Scripts/babysitting/avgfee_gnuplot.plt ``` # Open Source Development diff --git a/Scripts/babysitting/angular_power_spectrum.py b/Scripts/babysitting/angular_power_spectrum.py new file mode 100644 index 00000000..7e9f1f12 --- /dev/null +++ b/Scripts/babysitting/angular_power_spectrum.py @@ -0,0 +1,72 @@ +import numpy as np +import matplotlib.pyplot as plt +import glob +import h5py +import matplotlib as mpl +from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator,LogLocator) + +cmap=mpl.cm.jet + +################ +# plot options # +################ +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +#mpl.rc('text', usetex=True) +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 + +component_list = ["00","01","02","11","12","22"] + +f = h5py.File("reduced_data_angular_power_spectrum.h5","r") +t = np.array(f["t"]) +spectra = np.array(f["angular_spectrum"]) +f.close() + +tnorm = t[-1] + +def makeplot(icomponent,t, spectra): + plt.close('all') + fig, ax = plt.subplots(1,1, figsize=(8,6)) + + # get appropriate data + this_spectra = spectra[:,:,0,icomponent] + nl = np.shape(this_spectra)[1] + l = np.array(range(nl)) + for it in range(len(t)): + print(it,t) + total_power = np.sum(this_spectra[it]) + ax.semilogy(l, this_spectra[it,:], color=cmap(t[it]/tnorm)) + ax.grid() + + # colorbar + cax = fig.add_axes([0.125, .89, .775, 0.02]) + cax.tick_params(axis='both', which='both', direction='in', labeltop='on') + norm = mpl.colors.Normalize(vmin=0, vmax=tnorm) + cbar = plt.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),cax=cax,orientation='horizontal') + cbar.set_label(r"$t\,(10^{-9}\,\mathrm{s})$",labelpad=10) + cax.minorticks_on() + cax.xaxis.set_ticks_position('top') + cax.xaxis.set_label_position('top') + cax.xaxis.set_minor_locator(MultipleLocator(0.1)) + + # axis labels + ax.set_xlabel(r"$l$") + ax.set_ylabel(r"$|f_l|^2$") + ax.set_ylim(1e50, 1e61) + + plt.savefig("angular_power"+component_list[icomponent]+".pdf", bbox_inches='tight') + +nl = np.shape(spectra)[1] +for icomponent in range(len(component_list)): + print(component_list[icomponent]) + makeplot(icomponent, t, spectra) + diff --git a/Scripts/babysitting/avgfee.py b/Scripts/babysitting/avgfee.py new file mode 100755 index 00000000..40760a37 --- /dev/null +++ b/Scripts/babysitting/avgfee.py @@ -0,0 +1,81 @@ +# plots and as a function of time +# without reference to the reduced data file outputs + +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import numpy as np +import matplotlib.pyplot as plt +import glob +import h5py +import matplotlib as mpl +from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator,LogLocator) + + +base=["N","Fx","Fy","Fz"] +diag_flavor=["00","11"]#,"22"] +offdiag_flavor=["01"]#,"02","12"] +re=["Re","Im"] +# real/imag +R=0 +I=1 + + +###################### +# read averaged data # +###################### +def plotdata(filename,a,b): + avgData = h5py.File(filename,"r") + t=np.array(avgData["t(s)"])*1e9 + N=np.array(avgData["N_avg_mag(1|ccm)"])[:,a,b] + avgData.close() + return t, N + +################ +# plot options # +################ +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +#mpl.rc('text', usetex=True) +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 + + +fig, ax = plt.subplots(1,1, figsize=(6,5)) + +############## +# formatting # +############## +ax.axhline(1./3., color="green") +ax.set_ylabel(r"$\langle N\rangle_{ee}$ (cm$^{-3}$)") +ax.set_xlabel(r"$t\,(10^{-9}\,\mathrm{s})$") +ax.tick_params(axis='both', which='both', direction='in', right=True,top=True) +ax.xaxis.set_minor_locator(AutoMinorLocator()) +ax.yaxis.set_minor_locator(AutoMinorLocator()) +ax.minorticks_on() +ax.grid(which='both') + +filename = "plt_reduced_data.h5" +t,N = plotdata(filename,0,0) +ax.plot(t, N) +plt.savefig("avgfee.pdf", bbox_inches="tight") + +# same for f_e\mu +plt.cla() +ax.set_ylabel(r"$\langle N\rangle_\mathrm{offdiag}$ (cm$^{-3}$)") +ax.set_xlabel(r"$t\,(10^{-9}\,\mathrm{s})$") +ax.tick_params(axis='both', which='both', direction='in', right=True,top=True) +ax.xaxis.set_minor_locator(AutoMinorLocator()) +ax.yaxis.set_minor_locator(AutoMinorLocator()) +ax.minorticks_on() +ax.grid(which='both') +t,N = plotdata(filename,0,1) +ax.semilogy(t, N) +plt.savefig("avgfemu.pdf", bbox_inches="tight") diff --git a/Scripts/babysitting/avgfee_HDF5.py b/Scripts/babysitting/avgfee_HDF5.py new file mode 100755 index 00000000..71ea17a8 --- /dev/null +++ b/Scripts/babysitting/avgfee_HDF5.py @@ -0,0 +1,118 @@ +# plots and as a function of time +# assuming the code was compiled with HDF5 and wrote the file reduced0D.h5 + +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import numpy as np +import matplotlib.pyplot as plt +import glob +import h5py +import matplotlib as mpl +from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator,LogLocator) + + +base=["N","Fx","Fy","Fz"] +diag_flavor=["00","11"]#,"22"] +offdiag_flavor=["01"]#,"02","12"] +re=["Re","Im"] +# real/imag +R=0 +I=1 + + +###################### +# read averaged data # +###################### +avgData = h5py.File("reduced0D.h5","r") + +NF = 0 +if "N00(1|ccm)" in avgData.keys(): + NF = 2 +if "N11(1|ccm)" in avgData.keys(): + NF = 3 +assert(NF==2 or NF==3) + +t=np.array(avgData["time(s)"])*1e9 +N00=np.array(avgData["N00(1|ccm)"]) +N11=np.array(avgData["N11(1|ccm)"]) +N22=np.array(avgData["N22(1|ccm)"]) +N00bar=np.array(avgData["N00bar(1|ccm)"]) +N11bar=np.array(avgData["N11bar(1|ccm)"]) +N22bar=np.array(avgData["N22bar(1|ccm)"]) +Noffdiag = np.array(avgData["N_offdiag_mag(1|ccm)"]) +avgData.close() + +################ +# plot options # +################ +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +#mpl.rc('text', usetex=True) +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 + + +fig, ax = plt.subplots(1,1, figsize=(6,5)) + +############## +# formatting # +############## +ax.axhline(1./3., color="green") +ax.set_ylabel(r"$\langle N\rangle_{ee}$ (cm$^{-3}$)") +ax.set_xlabel(r"$t\,(10^{-9}\,\mathrm{s})$") +ax.tick_params(axis='both', which='both', direction='in', right=True,top=True) +ax.xaxis.set_minor_locator(AutoMinorLocator()) +ax.yaxis.set_minor_locator(AutoMinorLocator()) +ax.minorticks_on() +ax.grid(which='both') + +ax.plot(t, N00) +plt.savefig("avgfee.pdf", bbox_inches="tight") + +# same for f_e\mu +plt.cla() +ax.set_ylabel(r"$\langle N\rangle_\mathrm{offdiag}$ (cm$^{-3}$)") +ax.set_xlabel(r"$t\,(10^{-9}\,\mathrm{s})$") +ax.tick_params(axis='both', which='both', direction='in', right=True,top=True) +ax.xaxis.set_minor_locator(AutoMinorLocator()) +ax.yaxis.set_minor_locator(AutoMinorLocator()) +ax.minorticks_on() +ax.grid(which='both') + +ax.semilogy(t, Noffdiag) +plt.savefig("avgfemu.pdf", bbox_inches="tight") + +# plot ELN +plt.cla() +ax.set_ylabel(r"$\delta$ELN/$N_\mathrm{tot}$") +ax.set_xlabel(r"$t\,(10^{-9}\,\mathrm{s})$") +ax.tick_params(axis='both', which='both', direction='in', right=True,top=True) +ax.xaxis.set_minor_locator(AutoMinorLocator()) +ax.yaxis.set_minor_locator(AutoMinorLocator()) +ax.minorticks_on() +ax.grid(which='both') + +Ntot = N00+N11+N00bar+N11bar +if NF==3: + Ntot += N22+N22bar + +emu = (N00-N00bar) - (N11-N11bar) +ax.plot(t, (emu-emu[0])/Ntot,label=r"$e\mu$") + +etau = (N00-N00bar) - (N22-N22bar) +ax.plot(t, (etau-etau[0])/Ntot,label=r"$e\tau$") + +if NF==3: + mutau = (N11-N11bar) - (N22-N22bar) + ax.plot(t, (mutau-mutau[0])/Ntot,label=r"$\mu\tau$") + +plt.legend() +plt.savefig("ELN.pdf", bbox_inches="tight") diff --git a/Scripts/babysitting/avgfee_gnuplot.plt b/Scripts/babysitting/avgfee_gnuplot.plt new file mode 100644 index 00000000..234139eb --- /dev/null +++ b/Scripts/babysitting/avgfee_gnuplot.plt @@ -0,0 +1,7 @@ +set term pdf +set output 'avgfee.pdf' +set key autotitle columnhead +set xlabel 'time (s)' +set ylabel 'N00 (cm^-3)' +set yrange [0:*] +plot 'reduced0D.dat' u 2:5 w l \ No newline at end of file diff --git a/Scripts/babysitting/convert_reduced0D_to_hdf5.py b/Scripts/babysitting/convert_reduced0D_to_hdf5.py new file mode 100644 index 00000000..a8cf8047 --- /dev/null +++ b/Scripts/babysitting/convert_reduced0D_to_hdf5.py @@ -0,0 +1,21 @@ +import h5py +import numpy as np +import os + +def convert_reduced0D_to_hdf5(d): + infilename = d+"/reduced0D.dat" + with open(infilename,"r") as f: + labels = f.readline().split() + + data = np.genfromtxt(infilename, skip_header=1).transpose() + print(data.shape) + + outfilename = d+"/reduced0D.h5" + assert(not os.path.exists(outfilename)) + fout = h5py.File(outfilename,"w") + for i in range(len(labels)): + label = labels[i].split(":")[1] + fout[label] = data[i] + +if __name__ == '__main__': + convert_reduced0D_to_hdf5(".") diff --git a/Scripts/babysitting/has_crossing.py b/Scripts/babysitting/has_crossing.py new file mode 100644 index 00000000..d97fc1f2 --- /dev/null +++ b/Scripts/babysitting/has_crossing.py @@ -0,0 +1,50 @@ +import numpy as np +import sys +sys.path.append("/mnt/scratch/srichers/software/Emu/Scripts/data_reduction") +import amrex_plot_tools as amrex + +if len(sys.argv) != 2: + print("Usage: has_crossing.py particle_input.dat") + exit() + +filename = sys.argv[1] +print(filename) + +# read the number of flavors +f = open(filename,"r") +NF = int(f.readline()) +print(NF,"flavors") +f.close() + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + +# get the ELN info +data = np.genfromtxt(filename, skip_header=1) +nparticles = data.shape[0] + +N = data[:,rkey["N"]] +Nbar = data[:,rkey["Nbar"]] + +ndens = np.zeros((nparticles, 2,NF)) +suffixes = ["","bar"] +for i in range(2): + for j in range(NF): + Nname = "N"+suffixes[i] + fname = "f"+str(j)+str(j)+"_Re"+suffixes[i] + ndens[:,i,j] = data[:,rkey[Nname]] * data[:,rkey[fname]] + +for i in range(NF): + for j in range(i+1,NF): + lepdens_i = ndens[:,0,i] - ndens[:,1,i] + lepdens_j = ndens[:,0,j] - ndens[:,1,j] + eln = lepdens_i - lepdens_j + print(i,j,"crossing:") + mineln = np.min(eln) + maxeln = np.max(eln) + print(" min eln =",mineln) + print(" max eln =",maxeln) + if mineln*maxeln<0: + print('\033[92m UNSTABLE\x1b[0m') + else: + print(' stable') diff --git a/Scripts/babysitting/k_plot.py b/Scripts/babysitting/k_plot.py new file mode 100644 index 00000000..97f56e2a --- /dev/null +++ b/Scripts/babysitting/k_plot.py @@ -0,0 +1,118 @@ +import matplotlib.pyplot as mpl +import numpy as np +from scipy.signal import argrelextrema + +############## +# parameters # +############## +direction = "z" #direction of domain size. +base_directory = "" +abskplt = "lin" #"log" #switch between log and linear plots for abs(k) + +################ +# reading data # +################ +Lx=0 +Ly=0 +Lz=0 +inputs_file = open(base_directory + "inputs", 'r') +for line in inputs_file.readlines(): + if "ncell" in line: + str = line.split("(")[1].split(")")[0].split(",") + n = [int(str[0]), int(str[1]), int(str[2])] + if line[:2] == "Lx": + Lx = float(line.split("=")[1].split("#")[0]) + if line[:2] == "Ly": + Ly = float(line.split("=")[1].split("#")[0]) + if line[:2] == "Lz": + Lz = float(line.split("=")[1].split("#")[0]) + + L = [Lx, Ly, Lz] + +inputs_file.close() + +kmax = np.array(np.genfromtxt(base_directory + "kmax_t_N01.dat", skip_header=1)) +amp = kmax[:,4] +kz = kmax[:,3] +ky = kmax[:,2] +kx = kmax[:,1] +t = kmax[:,0]*10**6 + +# Obtaining the index at which exponential growth stops +flag = True # Flag to prevent any errors if the following method fails +indexes = np.concatenate([[0],argrelextrema(amp, np.greater)[0]]) # Take all the indexes at which there is a local maxima, as well as the first point in the data + +for i in range(len(indexes)-1): # Loop over all the indexes to check if there are two adjacent points that have a difference of three orders of magnitude + if abs(round((np.log10(amp[indexes[i]]/amp[indexes[i+1]])))) >= 3: + imax = indexes[i+1] + flag = False +if flag == True: # If there previous method does not work, the following is used + imax = np.argmax(amp) + +# Changing the length of the domain and kmin and kmax according to axis +if direction == "x": + i = 0 + labels = ["kmin for Lx", "kmax for Lx"] +elif direction == "y": + i = 1 + labels = ["kmin for Ly", "kmax for Ly"] +elif direction == "z": + i = 2 + labels = ["kmin for Lz", "kmax for Lz"] +kmn = 2*np.pi/L[i] +kmx = 2*np.pi/(2*L[i]/n[i]) + +################ +# plot options # +################ +mpl.rcParams['font.size'] = 17 +mpl.rcParams['font.family'] = 'serif' +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 +mpl.rcParams['figure.figsize'] = (10,10) + +################################ +# generating + formatting plot # +################################ +fig, axs = mpl.subplots(2, 1, sharex=True) +fig.subplots_adjust(hspace=0) + +axs[0].semilogy(t, amp) +axs[0].tick_params(axis='both',which="both", direction="in",top=True,right=True) +axs[0].minorticks_on() + +kmag = np.sqrt(kz**2+ky**2+kx**2) + +if abskplt == "log": + axs[1].semilogy(t, kmag) +elif abskplt == "lin": + axs[1].plot(t, kmag) +else: + axs[1].semilogy(t, kmag) + +axs[1].axhline(kmx, color='r', linestyle='-', label=labels[1]) +axs[1].axhline(kmn, color='g', linestyle='-', label=labels[0]) +axs[1].tick_params(axis='both',which="both", direction="in",top=True,right=True) +axs[1].minorticks_on() + +for ax in axs: + ax.axvline(t[imax]) + ax.axvline(2*t[imax]) + ax.axvline(3*t[imax]) + +axs[1].set_xlabel(r'time ($\mu s$)') +axs[0].set_ylabel(r'Amplitude (cm$^{-3}$)') +axs[1].set_ylabel(r'$|k|$ (cm$^{-1}$)') + +mpl.legend(frameon=False, labelspacing=0.25,fontsize=12, loc=(0.75,0.75)) + + +mpl.savefig('kmax.png') diff --git a/Scripts/babysitting/power_spectrum.py b/Scripts/babysitting/power_spectrum.py new file mode 100644 index 00000000..37b6b079 --- /dev/null +++ b/Scripts/babysitting/power_spectrum.py @@ -0,0 +1,72 @@ +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import numpy as np +import matplotlib.pyplot as plt +import glob +import h5py +import matplotlib as mpl +from matplotlib.ticker import (MultipleLocator, FormatStrFormatter,AutoMinorLocator,LogLocator) + +variables=["N","Fx","Fy","Fz"] +flavors=["00","11","22","01","02","12"] +cmap=mpl.cm.jet + +################ +# plot options # +################ +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +#mpl.rc('text', usetex=True) +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 + + +def makeplot(v,f,data): + + # get appropriate data + t=np.array(data["t(s)"]) + k=np.array(data["k(1|cm)"]) #FFT format not including 2 pi + fft = data[v+f+"_FFT(cm^-2)"] + total_power = np.sum(fft) + + plt.close('all') + fig, ax = plt.subplots(1,1, figsize=(8,6)) + for it in range(len(t)): + ax.semilogy(k, fft[it,:-1]/total_power, color=cmap(t[it]/t[-1])) + + # colorbar + cax = fig.add_axes([0.125, .89, .775, 0.02]) + cax.tick_params(axis='both', which='both', direction='in', labeltop='on') + norm = mpl.colors.Normalize(vmin=0, vmax=t[-1]*1e9) + cbar = plt.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),cax=cax,orientation='horizontal') + cbar.set_label(r"$t\,(10^{-9}\,\mathrm{s})$",labelpad=10) + cax.minorticks_on() + cax.xaxis.set_ticks_position('top') + cax.xaxis.set_label_position('top') + cax.xaxis.set_minor_locator(MultipleLocator(0.1)) + + # axis labels + ax.set_xlabel(r"$k\,(\mathrm{cm}^{-1})$") + ax.set_ylabel(r"$|\widetilde{f}|^2\,(\mathrm{cm}^{-2})$") + ax.minorticks_on() + ax.grid(which='both') + + plt.savefig(v+f+"_FFT_power.png", bbox_inches='tight') + +data = h5py.File("plt_reduced_data_fft_power.h5","r") + +for v in variables: + for f in flavors: + if v+f+"_FFT(cm^-2)" in data: + print(v+f) + makeplot(v,f, data) + +data.close() diff --git a/Scripts/collisions/compute_dE_coll_equi_test.py b/Scripts/collisions/compute_dE_coll_equi_test.py new file mode 100644 index 00000000..c0743e6d --- /dev/null +++ b/Scripts/collisions/compute_dE_coll_equi_test.py @@ -0,0 +1,47 @@ +''' +This script compute the energy bin size ( /Delta E ) for monoenergetic neutrino simulations given: +- Number of neutrinos at equilibrium +- Volume of a cell +- Number of momentum beams isotropically distributed per cell +- Neutrinos energy bin center +- Neutrino chemical potential +- Background matter temperature +This script was used to compute the energy bin size in the test scripts: coll_equi_test.py. +''' + +import numpy as np + +# constants +hbar = 1.05457266e-27 # erg s +h = hbar * 2 * np.pi # erg s +c = 2.99792458e10 # cm/s +hc = h * c # erg cm + +# Simulation parameters +V = 10**3 # Volume of a cell ( ccm ) +Ndir = 92 # Number of momentum beams isotropically distributed per cell +E = 50.0 # Neutrinos and antineutrinos energy bin center ( Mev ) +T = 10.0 # Background matter temperature ( Mev ) + +N_eq_electron_neutrino = 1e33 # Number of electron neutrinos at equilibrium +u_electron_neutrino = 0.0 # Electron neutrino chemical potential ( Mev ) + +# Fermi-dirac distribution factor for electron neutrinos +f_eq_electron_neutrinos = 1 / ( 1 + np.exp( ( E - u_electron_neutrino ) / T ) ) # adimentional + +# We know : +# dE^3 = 3 * Neq * ( hc )^ 3 / ( dV * dOmega * feq ) +delta_E_cubic = 3 * N_eq_electron_neutrino * ( hc )**3 / ( V * ( 4 * np.pi / Ndir ) * f_eq_electron_neutrinos ) # cubic erg +# dOmega = 4 * pi / ( number directions ) + +# We know polinomial of delta E in term of delta E cubic and E ( deltaE**3 = ( E + dE / 2)**3 - ( E - dE / 2)**3 ) +coeff = [ 0.25 , 0 , 3 * E**2 , -1.0 * delta_E_cubic / ( 1.60218e-6**3 ) ] +# Solving for this polinomial +deltaE = np.roots(coeff) + +# Extracting just the real root +dE=0 +for complex_deltaE in deltaE: + if (np.imag(complex_deltaE)==0): + print(f'Delta energy bin in MeV = {np.real(complex_deltaE)}') + dE=np.real(complex_deltaE) \ No newline at end of file diff --git a/Scripts/collisions/compute_dE_coll_inst_test.py b/Scripts/collisions/compute_dE_coll_inst_test.py new file mode 100644 index 00000000..dec22d2c --- /dev/null +++ b/Scripts/collisions/compute_dE_coll_inst_test.py @@ -0,0 +1,55 @@ +''' +This script compute the energy bin size ( /Delta E ) for monoenergetic neutrino simulations and the antineutrino chemical potential, given: +- Number of neutrinos at equilibrium +- Volume of a cell +- Number of momentum beams isotropically distributed per cell +- Neutrinos energy bin center +- Neutrino chemical potential +- Background matter temperature +This script was used to compute the energy bin size in the test scripts and antineutrino chemical potential: coll_inst_test.py. +''' + +import numpy as np + +# constants +hbar = 1.05457266e-27 # erg s +h = hbar * 2 * np.pi # erg s +c = 2.99792458e10 # cm/s +hc = h * c # erg cm + +# Simulation parameters +V = 1 # Volume of a cell ( ccm ) +Ndir = 92 # Number of momentum beams isotropically distributed per cell +E = 20.0 # Neutrinos and antineutrinos energy bin center ( Mev ) +T = 7.0 # Background matter temperature ( Mev ) + +N_eq_electron_neutrino = 3.260869565e+31 # Number of electron neutrinos at equilibrium +u_electron_neutrino = 20.0 # Electron neutrino chemical potential ( Mev ) + +# Fermi-dirac distribution factor for electron neutrinos +f_eq_electron_neutrinos = 1 / ( 1 + np.exp( ( E - u_electron_neutrino ) / T ) ) # adimentional + +# We know : +# dE^3 = 3 * Neq * ( hc )^ 3 / ( dV * dOmega * feq ) +delta_E_cubic = 3 * N_eq_electron_neutrino * ( hc )**3 / ( V * ( 4 * np.pi / Ndir ) * f_eq_electron_neutrinos ) # cubic erg +# dOmega = 4 * pi / ( number directions ) + +# We know polinomial of delta E in term of delta E cubic and E ( deltaE**3 = ( E + dE / 2)**3 - ( E - dE / 2)**3 ) +coeff = [ 0.25 , 0 , 3 * E**2 , -1.0 * delta_E_cubic / ( 1.60218e-6**3 ) ] +# Solving for this polinomial +deltaE = np.roots(coeff) + +# Extracting just the real root +dE=0 +for complex_deltaE in deltaE: + if (np.imag(complex_deltaE)==0): + print(f'Delta energy bin in MeV = {np.real(complex_deltaE)}') + dE=np.real(complex_deltaE) + +# Electron neutrino flavor +N_eq_electron_antineutrino = 2.717391304e+31 # Number of electron antineutrinos at equilibrium + +# Computing electron antineutrino chemical potential +f_eq_electron_antineutrino = 3 * N_eq_electron_antineutrino * ( hc )**3 / ( V * ( 4 * np.pi / Ndir ) * delta_E_cubic ) +u_electron_antineutrino = E - T * np.log( 1 / f_eq_electron_antineutrino - 1 ) +print(f'Electron neutrino chemical potential in MeV = {u_electron_antineutrino}') \ No newline at end of file diff --git a/Scripts/collisions/nsm_constant_background_rho_Ye_T__writer.py b/Scripts/collisions/nsm_constant_background_rho_Ye_T__writer.py new file mode 100644 index 00000000..8a336ed9 --- /dev/null +++ b/Scripts/collisions/nsm_constant_background_rho_Ye_T__writer.py @@ -0,0 +1,47 @@ +''' +Created by Erick Urquilla, Department of Physics and Astronomy, University of Tennessee, Knoxville. +This script is used to generate an HDF5 file that contains constant background matter information: rho (density), Yₑ (electron fraction), and T (temperature). +The HDF5 file generated by this script will be used as input for background matter quantities in EMU. +''' + +import numpy as np +import h5py +import nsm_grid_generator + +# EMU grid parameters +ncellsx = 2 # scalar, number of cells in x-direction +ncellsy = 2 # scalar, number of cells in y-direction +ncellsz = 2 # scalar, number of cells in z-direction +xmin = 0.0 #cm +xmax = 2.0e4 #cm +ymin = 0.0 #cm +ymax = 2.0e4 #cm +zmin = 0.0 #cm +zmax = 2.0e4 #cm + +# Create EMU mesh +centers, mesh = nsm_grid_generator.create_grid([ncellsx, ncellsy, ncellsz], [[xmin, xmax], [ymin, ymax], [zmin, zmax]]) # cm + +rho_cons = 1802418929.1457505 # g/ccm +T_cons = 5.0246353 # MeV +Ye_cons = 0.46005958 # n_electron - n_positron / n_barions + +# Create arrays to store the interpolated values of T, rho, and Ye. +rho = np.full( ( ncellsx, ncellsy, ncellsz ), rho_cons ) # array of size (ncellsx, ncellsy, ncellsz) +T = np.full( ( ncellsx, ncellsy, ncellsz ), T_cons ) # array of size (ncellsx, ncellsy, ncellsz) +Ye = np.full( ( ncellsx, ncellsy, ncellsz ), Ye_cons ) # array of size (ncellsx, ncellsy, ncellsz) + +# Write hdf5 file with all the data +with h5py.File('rho_Ye_T.hdf5', 'w') as hdf: + hdf.create_dataset("ncellsx", data=ncellsx) + hdf.create_dataset("ncellsy", data=ncellsy) + hdf.create_dataset("ncellsz", data=ncellsz) + hdf.create_dataset("xmin_cm", data=xmin) + hdf.create_dataset("xmax_cm", data=xmax) + hdf.create_dataset("ymin_cm", data=ymin) + hdf.create_dataset("ymax_cm", data=ymax) + hdf.create_dataset("zmin_cm", data=zmin) + hdf.create_dataset("zmax_cm", data=zmax) + hdf.create_dataset("rho_g|ccm", data=rho) + hdf.create_dataset("T_Mev", data=T) + hdf.create_dataset("Ye", data=Ye) \ No newline at end of file diff --git a/Scripts/collisions/nsm_grid_generator.py b/Scripts/collisions/nsm_grid_generator.py new file mode 100644 index 00000000..40c88028 --- /dev/null +++ b/Scripts/collisions/nsm_grid_generator.py @@ -0,0 +1,42 @@ +''' +Created by Erick Urquilla, Department of Physics and Astronomy, University of Tennessee, Knoxville. +This script is used to Creates a 3D cartesian grid. +''' + +import numpy as np + +def create_grid(cell_numbers, dimensions): + """ + Creates a 3D grid of points and corresponding mesh for the given cell numbers and dimensions. + + Parameters: + - cell_numbers: A list or tuple of integers specifying the number of cells along each dimension (x, y, z). + - dimensions: A list or tuple of tuples, where each inner tuple contains the minimum and maximum value for that dimension (e.g., [(xmin, xmax), (ymin, ymax), (zmin, zmax)]). + + Returns: + - centers: A 2D array where each row is the (x, y, z) coordinate of a grid point center. + - mesh: A 3D array containing the mesh grid of coordinates for each dimension. + """ + + # Create arrays of face positions along each dimension + # 'faces' will contain arrays that specify the boundaries of cells in each dimension + faces = [np.linspace(d[0], d[1], n+1) for d, n in zip(dimensions, cell_numbers)] + + # Calculate the center positions of the cells in each dimension + # 'centers' will contain arrays of the center points between the cell boundaries (faces) + centers = [0.5 * (f[1:] + f[:-1]) for f in faces] + + # Create a 3D mesh grid of center points using the calculated centers for each dimension + # 'indexing="ij"' is used to maintain matrix indexing (row-major order) + X, Y, Z = np.meshgrid(*centers, indexing='ij') + + # Flatten the 3D mesh grid to obtain a 2D array of (x, y, z) coordinates for each grid point + # Each row in 'centers' corresponds to the coordinates of a single grid point + centers = np.vstack((X.flatten(), Y.flatten(), Z.flatten())).transpose() + + # Combine the 3D mesh grid into a single array 'mesh' for easy access + # 'mesh' will be a 3D array containing the X, Y, and Z coordinates of the grid + mesh = np.stack((X, Y, Z), axis=-1) + + # Return the grid point centers and the mesh grid + return centers, mesh \ No newline at end of file diff --git a/Scripts/collisions/writeparticleinfohdf5.py b/Scripts/collisions/writeparticleinfohdf5.py new file mode 100755 index 00000000..1a0f0187 --- /dev/null +++ b/Scripts/collisions/writeparticleinfohdf5.py @@ -0,0 +1,133 @@ +########################################################## +#This script write all the particle information in the plt* directories into hdf5 format files +########################################################## + +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+'/data_reduction') +import numpy as np +import matplotlib.pyplot as plt +import yt +import glob +import multiprocessing as mp +import h5py +import amrex_plot_tools as amrex +import emu_yt_module as emu +from multiprocessing import Pool +import scipy.special + +########## +# INPUTS # +########## +nproc = 1 + +dirh5 = sorted(glob.glob("*.h5")) +dirh5 = [dirh5[i].split('.')[0] for i in range(len(dirh5))] +dirall = sorted(glob.glob("plt*/neutrinos")) +dirall = [dirall[i].split('/')[0] for i in range(len(dirall))] # remove "neutrinos" + +directories=[] + +for dir1 in dirall: + thereis=0 + for dir2 in dirh5: + if dir1==dir2: + thereis=1 + break + if thereis==0: + directories.append(dir1) + +# get NF +eds = emu.EmuDataset(directories[0]) +NF = eds.get_num_flavors() +if NF==2: + rkey, ikey = amrex.get_particle_keys(NF) + labels=['pos_x','pos_y','pos_z', 'time', 'x', 'y', 'z', 'pupx', 'pupy', 'pupz', 'pupt', 'N00_Re', 'N01_Re', 'N01_Im', 'N11_Re', 'N00_Rebar', 'N01_Rebar', 'N01_Imbar', 'N11_Rebar', 'TrHN', 'Vphase'] +if NF==3: + rkey, ikey = amrex.get_particle_keys(NF) + labels=['pos_x','pos_y','pos_z','time','x', 'y', 'z', 'pupx', 'pupy', 'pupz', 'pupt', 'N00_Re', 'N01_Re', 'N01_Im', 'N02_Re', 'N02_Im', 'N11_Re', 'N12_Re', 'N12_Im' ,'N22_Re', 'N00_Rebar', 'N01_Rebar', 'N01_Imbar', 'N02_Rebar', 'N02_Imbar', 'N11_Rebar', 'N12_Rebar' ,'N12_Imbar', 'N22_Rebar', 'TrHN', 'Vphase'] + +class GridData(object): + def __init__(self, ad): + x = ad['index','x'].d + y = ad['index','y'].d + z = ad['index','z'].d + dx = ad['index','dx'].d + dy = ad['index','dy'].d + dz = ad['index','dz'].d + self.ad = ad + self.dx = dx[0] + self.dy = dy[0] + self.dz = dz[0] + self.xmin = np.min(x-dx/2.) + self.ymin = np.min(y-dy/2.) + self.zmin = np.min(z-dz/2.) + self.xmax = np.max(x+dx/2.) + self.ymax = np.max(y+dy/2.) + self.zmax = np.max(z+dz/2.) + self.nx = int((self.xmax - self.xmin) / self.dx + 0.5) + self.ny = int((self.ymax - self.ymin) / self.dy + 0.5) + self.nz = int((self.zmax - self.zmin) / self.dz + 0.5) + print(self.nx, self.ny, self.nz) + + def get_particle_cell_ids(self,rdata): + # get coordinates + x = rdata[:,rkey["x"]] + y = rdata[:,rkey["y"]] + z = rdata[:,rkey["z"]] + ix = (x/self.dx).astype(int) + iy = (y/self.dy).astype(int) + iz = (z/self.dz).astype(int) + + # HACK - get this grid's bounds using particle locations + ix -= np.min(ix) + iy -= np.min(iy) + iz -= np.min(iz) + nx = np.max(ix)+1 + ny = np.max(iy)+1 + nz = np.max(iz)+1 + idlist = (iz + nz*iy + nz*ny*ix).astype(int) + + return idlist + +def writehdf5files(dire): + + eds = emu.EmuDataset(dire) + t = eds.ds.current_time + ad = eds.ds.all_data() + + header = amrex.AMReXParticleHeader(dire+"/neutrinos/Header") + grid_data = GridData(ad) + nlevels = len(header.grids) + assert nlevels==1 + level = 0 + ngrids = len(header.grids[level]) + + # creating the file to save the particle data + hf = h5py.File(str(dire)+".h5", 'w') + + for label in labels: + hf.create_dataset(label,data=[],maxshape=(None,),chunks=True) + + # loop over all cells within each grid + for gridID in range(ngrids): + + # read particle data on a single grid + idata, rdata = amrex.read_particle_data(dire, ptype="neutrinos", level_gridID=(level,gridID)) + + # writing the particle data + for label in labels: + hf[label].resize((hf[label].shape[0] + rdata[:,rkey[label]].shape[0]), axis=0) + hf[label][-rdata[:,rkey[label]].shape[0]:] = rdata[:,rkey[label]] + + hf.close() + + return dire + +# run the write hdf5 files function in parallel +if __name__ == '__main__': + pool = Pool(nproc) + finalresult=pool.map(writehdf5files,directories) + for i in finalresult: print("completed ---> "+i) + diff --git a/Scripts/data_reduction/.gitignore b/Scripts/data_reduction/.gitignore new file mode 100644 index 00000000..c24e9012 --- /dev/null +++ b/Scripts/data_reduction/.gitignore @@ -0,0 +1,2 @@ +*~ +__pycache__ \ No newline at end of file diff --git a/Scripts/visualization/amrex_plot_tools.py b/Scripts/data_reduction/amrex_plot_tools.py similarity index 59% rename from Scripts/visualization/amrex_plot_tools.py rename to Scripts/data_reduction/amrex_plot_tools.py index a05ab691..97e7a65a 100644 --- a/Scripts/visualization/amrex_plot_tools.py +++ b/Scripts/data_reduction/amrex_plot_tools.py @@ -9,76 +9,69 @@ mp = 1.6726219e-24 # g GF = 1.1663787e-5 / (1e9*eV)**2 * (hbar*clight)**3 #erg cm^3 -def get_particle_keys(): - real_quantities = ["pos_x", - "pos_y", - "pos_z", - "time", - "x", - "y", - "z", - "pupx", - "pupy", - "pupz", - "pupt", - "N", - "L", - "f00_Re", - "f01_Re", - "f01_Im", - "f11_Re", - "Nbar", - "Lbar", - "f00_Rebar", - "f01_Rebar", - "f01_Imbar", - "f11_Rebar"] - - rkey = {} - for i, rlabel in enumerate(real_quantities): - rkey[rlabel] = i - - ikey = { - # no ints are stored - } - - return rkey, ikey - -def get_3flavor_particle_keys(): - real_quantities = ["pos_x", - "pos_y", - "pos_z", - "time", - "x", - "y", - "z", - "pupx", - "pupy", - "pupz", - "pupt", - "N", - "L", - "f00_Re", - "f01_Re", - "f01_Im", - "f02_Re", - "f02_Im", - "f11_Re", - "f12_Re", - "f12_Im", - "f22_Re", - "Nbar", - "Lbar", - "f00_Rebar", - "f01_Rebar", - "f01_Imbar", - "f02_Rebar", - "f02_Imbar", - "f11_Rebar", - "f12_Rebar", - "f12_Imbar", - "f22_Rebar"] - +# NF is the number of flavors +# ignore_pos causes the first three elements to be ignored +# xp_only returns the position and momentum keys, but not the f keys +def get_particle_keys(NF, ignore_pos=False, xp_only=False): + assert(NF==2 or NF==3) + if(NF==2): + real_quantities = ["pos_x", + "pos_y", + "pos_z", + "time", + "x", + "y", + "z", + "pupx", + "pupy", + "pupz", + "pupt", + "N00_Re", + "N01_Re", + "N01_Im", + "N11_Re", + "N00_Rebar", + "N01_Rebar", + "N01_Imbar", + "N11_Rebar", + "TrHN", + "Vphase"] + if(NF==3): + real_quantities = ["pos_x", + "pos_y", + "pos_z", + "time", + "x", + "y", + "z", + "pupx", + "pupy", + "pupz", + "pupt", + "N00_Re", + "N01_Re", + "N01_Im", + "N02_Re", + "N02_Im", + "N11_Re", + "N12_Re", + "N12_Im", + "N22_Re", + "N00_Rebar", + "N01_Rebar", + "N01_Imbar", + "N02_Rebar", + "N02_Imbar", + "N11_Rebar", + "N12_Rebar", + "N12_Imbar", + "N22_Rebar", + "TrHN", + "Vphase"] + + if xp_only: real_quantities = real_quantities[:11] + if ignore_pos: real_quantities = real_quantities[7:] + rkey = {} for i, rlabel in enumerate(real_quantities): rkey[rlabel] = i @@ -156,7 +149,7 @@ def __init__(self, header_filename): self.grids[level_num].append(tuple(entry)) -def read_particle_data(fn, ptype="particle0"): +def read_particle_data(fn, ptype="particle0", level_gridID=None): ''' This function returns the particle data stored in a particular @@ -181,11 +174,22 @@ def read_particle_data(fn, ptype="particle0"): elif header.real_type == np.float32: fdtype = "(%d,)f4" % header.num_real - idata = np.empty((header.num_particles, header.num_int )) - rdata = np.empty((header.num_particles, header.num_real)) + if level_gridID==None: + level_gridlist = enumerate(header.grids) + idata = np.empty((header.num_particles, header.num_int )) + rdata = np.empty((header.num_particles, header.num_real)) + else: + level = level_gridID[0] + gridID = level_gridID[1] + level_grid = header.grids[level][gridID] + which, count, where = level_grid + level_gridlist = enumerate([[level_grid,],]) + idata = np.empty((count, header.num_int )) + rdata = np.empty((count, header.num_real)) + ip = 0 - for lvl, level_grids in enumerate(header.grids): + for lvl, level_grids in level_gridlist: for (which, count, where) in level_grids: if count == 0: continue fn = base_fn + "/Level_%d/DATA_%05d" % (lvl, which) diff --git a/Scripts/data_reduction/combine_files.py b/Scripts/data_reduction/combine_files.py new file mode 100644 index 00000000..12de9c8c --- /dev/null +++ b/Scripts/data_reduction/combine_files.py @@ -0,0 +1,46 @@ +import glob +import h5py +import numpy as np +import sys + +def combine_files(directory, filename_base, filename_tail): + file_list = sorted(glob.glob(directory+"/"+filename_base+"*"+filename_tail)) + + # get the number of datasets in the file + f = h5py.File(file_list[0],"r") + keylist = [key for key in f.keys()] + ndatasets = len(keylist) + f.close() + + # collect the data in appended arrays + print() + datasets = [[] for i in range(ndatasets)] + for filename in file_list: + print("getting data from",filename) + f = h5py.File(filename,"r") + for i, key in enumerate(keylist): + datasets[i].append(np.array(f[key])) + f.close() + + # concatenate the arrays together + # output to file + print() + output_filename = directory+"/"+filename_base+filename_tail + print("Outputting datasets to "+output_filename) + f = h5py.File(filename_base+filename_tail,"w") + for i, key in enumerate(keylist): + if key=="k(1|cm)" or key=="phat": + datasets[i] = datasets[i][0] + else: + datasets[i] = np.concatenate(datasets[i], axis=0) + f[key] = datasets[i] + print(key, "\t",np.shape(datasets[i]) ) + f.close() + +if __name__ == "__main__": + if(len(sys.argv) != 3): + print() + print("Usage: [combine_files.py filename_base filename_tail], where filename is contained in each of the run* subdirectories") + print() + exit() + combine_files(".", sys.argv[1], sys.argv[2]) diff --git a/Scripts/data_reduction/convertToHDF5.py b/Scripts/data_reduction/convertToHDF5.py new file mode 100755 index 00000000..6489aaa8 --- /dev/null +++ b/Scripts/data_reduction/convertToHDF5.py @@ -0,0 +1,88 @@ +# used to make plots but now just generates a hdf5 file with domain-averaged data. +# Run in the directory of the simulation the data should be generated for. +# Still has functionality for per-snapshot plots, but the line is commented out. +# This version averages the magnitudes of off-diagonal components rather than the real/imaginary parts +# also normalizes fluxes by sumtrace of N rather than F. +# This data is used for the growth plot. +# Note - also tried a version using maxima rather than averages, and it did not make the growth plot look any better. + +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import sys +sys.path.append(os.path.dirname(os.path.abspath(__file__))) +import numpy as np +import matplotlib.pyplot as plt +import yt +import glob +import multiprocessing as mp +import h5py +import amrex_plot_tools as amrex +import emu_yt_module as emu +from multiprocessing import Pool +import scipy.special +import shutil + +# NOTE - assumes particle output is done at a multiple of fluid output + +######## +# misc # +######## +fluid_vars = ["N","Fx","Fy","Fz"] +nunubar = ["","bar"] + +def convert_to_HDF5(sim_directory, DELETE_ALL_BUT_LAST_RESTART=False): + ######################### + # loop over directories # + ######################### + fluid_directories = sorted(glob.glob(sim_directory+"/plt?????")) + nfluid = len(fluid_directories) + + print(fluid_directories) + + for d in fluid_directories: + print(d) + eds = emu.EmuDataset(d) + t = eds.ds.current_time + ad = eds.ds.all_data() + datatype = ad['boxlib',"N00_Re"].d.dtype + + if d==fluid_directories[0]: + NF = eds.get_num_flavors() + allData = h5py.File(sim_directory+"/allData.h5","w") + allData["dz(cm)"] = eds.dz + allData.create_dataset("t(s)", data=np.zeros(0), maxshape=(None,), dtype=datatype) + allData.create_dataset("it", data=np.zeros(0), maxshape=(None,), dtype=int) + + # create fluid data sets + maxshape = (None, eds.Nz) + chunkshape = (1, eds.Nz) + zeros = np.zeros((0,eds.Nz)) + varlist = [] + for v in fluid_vars: + for f1 in range(NF): + for f2 in range(f1,NF): + for b in nunubar: + varlist.append(v+str(f1)+str(f2)+"_Re"+b+"(1|ccm)") + if f2!=f1: + varlist.append(v+str(f1)+str(f2)+"_Im"+b+"(1|ccm)") + for v in varlist: + allData.create_dataset(v, data=zeros, maxshape=maxshape, chunks=chunkshape, dtype=datatype) + + # resize the datasets + allData["t(s)"].resize((len(allData["t(s)"]) + 1, )) + allData["t(s)"][-1] = eds.ds.current_time + allData["it"].resize((len(allData["it"]) + 1, )) + allData["it"][-1] = int(d[-5:]) + for v in varlist: + allData[v].resize(np.shape(allData[v])[0] + 1, axis=0) + allData[v][-1,:] = eds.cg[v[:-7]].d + + if DELETE_ALL_BUT_LAST_RESTART: + particle_directories = [d[:-10] for d in sorted(glob.glob(sim_directory+"/plt*/neutrinos"))] + last_particle_directory = particle_directories[-1] + for d in fluid_directories: + if d != last_particle_directory: + shutil.rmtree(d) + +if __name__ == "__main__": + convert_to_HDF5(".") diff --git a/Scripts/data_reduction/emu_yt_module.py b/Scripts/data_reduction/emu_yt_module.py new file mode 100755 index 00000000..79c620cf --- /dev/null +++ b/Scripts/data_reduction/emu_yt_module.py @@ -0,0 +1,382 @@ +import yt +import numpy as np +import math +import scipy.fft as fft +from yt import derived_field +import yt.units.dimensions as dimensions + +class Dim3(object): + def __init__(self, x=0, y=0, z=0): + if type(x) == np.ndarray: + self.x = x[0] + self.y = x[1] + self.z = x[2] + else: + self.x = x + self.y = y + self.z = z + + @staticmethod + def dimensions(lo, hi): + # return the number of elements (lo, hi) + # contain along each dimension + return [hi.x - lo.x + 1, + hi.y - lo.y + 1, + hi.z - lo.z + 1] + +class FourierData(object): + def __init__(self, time, kx, ky, kz, FT_magnitude, FT_phase): + # Dataset time + self.time = time + + # Wavenumbers + self.kx = kx + self.ky = ky + self.kz = kz + + # Fourier transform magnitude & phase + self.magnitude = FT_magnitude + self.phase = FT_phase + +class EmuDataset(object): + def __init__(self, plotfile=None): + # initialize from a supplied plotfile, otherwise just make an empty dataset object + # and initialize it later with init_from_data() + if plotfile: + self.setup_dataset(yt.load(plotfile)) + else: + self.ds = None + + def init_from_data(self, data, left_edge=None, right_edge=None, sim_time=0.0, dimensions=None, + length_unit=(1.0, "cm"), periodicity=(True, True, True), nprocs=1): + + assert(left_edge is not None and right_edge is not None) + + # initialize the dataset using a dictionary of numpy arrays in data, keyed by field name + domain_bounds = np.array([[left_edge[0], right_edge[0]], + [left_edge[1], right_edge[1]], + [left_edge[2], right_edge[2]]]) + + self.setup_dataset(yt.load_uniform_grid(data, dimensions, length_unit=length_unit, sim_time=sim_time, + bbox=domain_bounds, periodicity=periodicity, nprocs=nprocs)) + + def setup_dataset(self, yt_dataset): + self.ds = yt_dataset + self.construct_covering_grid() + #self.add_emu_fields() + + def construct_covering_grid(self): + self.cg = self.ds.covering_grid(level=0, left_edge=self.ds.domain_left_edge, + dims=self.ds.domain_dimensions) + + # number of cells in each dimension + self.Nx = self.ds.domain_dimensions[0] + self.Ny = self.ds.domain_dimensions[1] + self.Nz = self.ds.domain_dimensions[2] + + # find dx, dy, dz in each of X,Y,Z + # this is the spacing between cell centers in the domain + # it is the same as the spacing between cell edges + self.dx = (self.ds.domain_right_edge[0] - self.ds.domain_left_edge[0])/self.ds.domain_dimensions[0] + self.dy = (self.ds.domain_right_edge[1] - self.ds.domain_left_edge[1])/self.ds.domain_dimensions[1] + self.dz = (self.ds.domain_right_edge[2] - self.ds.domain_left_edge[2])/self.ds.domain_dimensions[2] + + if self.Nx > 1: + # low, high edge locations in x domain + xlo = self.ds.domain_left_edge[0] + xhi = self.ds.domain_right_edge[0] + + # the offset between the edges xlo, xhi and the interior cell centers + x_cc_offset = 0.5 * self.dx + + self.X, DX = np.linspace(xlo + x_cc_offset, # first cell centered location in the interior of x domain + xhi - x_cc_offset, # last cell centered location in the interior of x domain + num=self.Nx, # Nx evenly spaced samples + endpoint=True, # include interval endpoint for the last cell-centered location in the domain + retstep=True) # return the stepsize between cell centers to check consistency with dx + + # the spacing we calculated should be the same as what linspace finds between cell centers + # using our edge-to-cell-center offset and the number of samples + #print("dx, DX = ", dx, DX) + assert math.isclose(self.dx,DX) + + if self.Ny > 1: + # low, high edge locations in y domain + ylo = self.ds.domain_left_edge[1] + yhi = self.ds.domain_right_edge[1] + + # the offset between the edges ylo, yhi and the interior cell centers + y_cc_offset = 0.5 * self.dy + + self.Y, DY = np.linspace(ylo + y_cc_offset, # first cell centered location in the interior of y domain + yhi - y_cc_offset, # last cell centered location in the interior of y domain + num=self.Ny, # Ny evenly spaced samples + endpoint=True, # include interval endpoint for the last cell-centered location in the domain + retstep=True) # return the stepsize between cell centers to check consistency with dy + + # the spacing we calculated should be the same as what linspace finds between cell centers + # using our edge-to-cell-center offset and the number of samples + #print("dy, DY = ", dy, DY) + assert math.isclose(self.dy,DY) + + + if self.Nz > 1: + # low, high edge locations in z domain + zlo = self.ds.domain_left_edge[2] + zhi = self.ds.domain_right_edge[2] + + # the offset between the edges zlo, zhi and the interior cell centers + z_cc_offset = 0.5 * self.dz + + self.Z, DZ = np.linspace(zlo + z_cc_offset, # first cell centered location in the interior of z domain + zhi - z_cc_offset, # last cell centered location in the interior of z domain + num=self.Nz, # Nz evenly spaced samples + endpoint=True, # include interval endpoint for the last cell-centered location in the domain + retstep=True) # return the stepsize between cell centers to check consistency with dz + + # the spacing we calculated should be the same as what linspace finds between cell centers + # using our edge-to-cell-center offset and the number of samples + #print("dz, DZ = ", dz, DZ) + assert math.isclose(self.dz,DZ) + + def get_num_flavors(self): + just_the_fields = [f for ftype, f in self.ds.field_list] + if "N33_Re" in just_the_fields: + raise NotImplementedError("Analysis script currently only supports 2 and 3 flavor simulations") + elif "N22_Re" in just_the_fields: + return 3 + else: + return 2 + + def get_num_dimensions(self): + dim = self.ds.domain_dimensions + return np.sum(dim > 1) + + #def add_emu_fields(self): + # # first, define the trace + # def _make_trace(ds): + # if self.get_num_flavors() == 3: + # def _trace(field, data): + # return data["N00_Re"] + data["N11_Re"] + data["N22_Re"] + # return _trace + # else: + # def _trace(field, data): + # return data["N00_Re"] + data["N11_Re"] + # return _trace + # + # _trace = _make_trace(self.ds) + # + # self.ds.add_field(("gas", "trace"), function=_trace, sampling_type="local", units="auto", dimensions=dimensions.dimensionless) + # + # # now, define normalized fields + # for f in self.ds.field_list: + # if "_Re" in f[1] or "_Im" in f[1]: + # fname = f[1] + # fname_norm = "{}_norm_tr".format(fname) + # + # def _make_derived_field(f): + # def _derived_field(field, data): + # return data[f]/data[("gas", "trace")] + # return _derived_field + # + # _norm_derived_f = _make_derived_field(f) + # self.ds.add_field(("gas", fname_norm), function=_norm_derived_f, sampling_type="local", units="auto", dimensions=dimensions.dimensionless) + + def fourier(self, field_Re, field_Im=None, nproc=None): + if field_Im: + FT = np.squeeze(self.cg[field_Re][:,:,:].d + 1j * self.cg[field_Im][:,:,:].d) + else: + FT = np.squeeze(self.cg[field_Re][:,:,:].d) + + # use fftn to do an N-dimensional FFT on an N-dimensional numpy array + FT = fft.fftn(FT,workers=nproc) + + # we're shifting the sampling frequencies next, so we have to shift the FFT values + FT = fft.fftshift(FT) + + # get the absolute value of the fft + FT_mag = np.abs(FT) + + # get the phase of the fft + FT_phi = np.angle(FT) + + if self.Nx > 1: + # find the sampling frequencies in X & shift them + kx = fft.fftfreq(self.Nx, self.dx) + kx = fft.fftshift(kx) + else: + kx = None + + if self.Ny > 1: + # find the sampling frequencies in Y & shift them + ky = fft.fftfreq(self.Ny, self.dy) + ky = fft.fftshift(ky) + else: + ky = None + + if self.Nz > 1: + # find the sampling frequencies in Z & shift them + kz = fft.fftfreq(self.Nz, self.dz) + kz = fft.fftshift(kz) + else: + kz = None + + return FourierData(self.ds.current_time, kx, ky, kz, FT_mag, FT_phi) + + def get_rectangle(self, left_edge, right_edge): + # returns an EmuDataset containing only the rectangular region + # defined by [left_edge, right_edge] in the current dataset + + data, data_dimensions = self.get_data(bounds=(left_edge, right_edge)) + + # return a new EmuDataset object + new_dataset = EmuDataset() + new_dataset.init_from_data(data, left_edge=left_edge, right_edge=right_edge, sim_time=self.ds.current_time.in_units("s"), + dimensions=data_dimensions, length_unit=self.ds.length_unit, + periodicity=(False, False, False), nprocs=1) + return new_dataset + + def get_data(self, bounds=None): + # gets a dictionary of numpy arrays with the raw data in this dataset, keyed by field + # if bounds = None, returns the entire domain, otherwise interpret bounds = (left_edge, right_edge) + # where left_edge and right_edge are each numpy arrays with the physical positions of the selection edges + # and return the subdomain inside those bounds at the same resolution as the original dataset + cg = self.ds.covering_grid(left_edge=self.ds.domain_left_edge, dims=self.ds.domain_dimensions, level=0) + + ddim = self.ds.domain_dimensions + + # lo, hi Dim3's are defined in the AMReX style where they are inclusive indices + # defined for Fortran-style array slicing (not Python) + lo = Dim3(0,0,0) + hi = Dim3(ddim - 1) + + if bounds: + left_edge, right_edge = bounds + dleft = self.ds.domain_left_edge + dright = self.ds.domain_right_edge + delta = [self.dx, self.dy, self.dz] + + # initialize lo to the first indices in the domain + lo = np.array([0,0,0]) + # initialize hi to the last indices in the domain + hi = ddim - 1 + + for i in range(3): + if ddim[i] > 1: + # set lo[i] to the first cell-centered index to the right of left_edge + lo[i] = round((left_edge[i].d - dleft[i].d) / delta[i].d) + # set hi[i] to the last cell-centered index to the left of right_edge + hi[i] = hi[i] - round((dright[i].d - right_edge[i].d) / delta[i].d) + + lo = Dim3(lo) + hi = Dim3(hi) + + data = {} + data_dimensions = Dim3.dimensions(lo, hi) + + # Note: we have to throw away the field type, e.g. 'boxlib' because YT's uniform data loader + # gives its fields a 'stream' type. If we were to keep the field type here, we would be unable + # to call to_3D() on a dataset returned by to_2D() since the field types would not match. + for ftype, f in self.ds.field_list: + data[f] = (cg[f][lo.x:hi.x+1, lo.y:hi.y+1, lo.z:hi.z+1].d, "") + + return data, data_dimensions + + def to_2D(self, extend_dims=None): + # transform this 1D dataset into a 2D EmuDataset object + + # first, assert this dataset is 1D along z + assert(self.ds.domain_dimensions[0] == 1 and self.ds.domain_dimensions[1] == 1 and self.ds.domain_dimensions[2] > 1) + + # get the 1D data + data, _ = self.get_data() + + data_2D = {} + + # by default extend y to match z unless extend_dims is passed + length_z = self.ds.domain_dimensions[2] + length_y = length_z + if extend_dims: + length_y = extend_dims + + for f in data.keys(): + df, df_units = data[f] + data_2D[f] = (np.tile(df, (1, length_y, 1)), df_units) + + left_edge = self.ds.domain_left_edge + left_edge[1] = left_edge[2] + right_edge = self.ds.domain_right_edge + right_edge[1] = right_edge[2] + dimensions = self.ds.domain_dimensions + dimensions[1] = length_y + + # return a new EmuDataset object + new_dataset = EmuDataset() + new_dataset.init_from_data(data_2D, left_edge=left_edge, right_edge=right_edge, sim_time=self.ds.current_time.in_units("s"), + dimensions=dimensions, length_unit=self.ds.length_unit, + periodicity=self.ds.periodicity, nprocs=1) + return new_dataset + + def to_3D(self, extend_dims=None): + # transform this 1D or 2D dataset into a 3D EmuDataset object + + # check if this dataset is 1D or 2D and extended along z + assert(self.ds.domain_dimensions[0] == 1 and self.ds.domain_dimensions[2] > 1) + + # get the current data + data, _ = self.get_data() + + data_3D = {} + left_edge = None + right_edge = None + dimensions = None + + if self.ds.domain_dimensions[1] == 1: + # we are 1D, extended along z + # by default extend x, y to match z unless extend_dims is passed + length_z = self.ds.domain_dimensions[2] + length_y = length_z + length_x = length_z + if extend_dims: + length_x, length_y = extend_dims + + for f in data.keys(): + df, df_units = data[f] + data_3D[f] = (np.tile(df, (length_x, length_y, 1)), df_units) + + left_edge = self.ds.domain_left_edge + left_edge[0] = left_edge[2] + left_edge[1] = left_edge[2] + right_edge = self.ds.domain_right_edge + right_edge[0] = right_edge[2] + right_edge[1] = right_edge[2] + dimensions = self.ds.domain_dimensions + dimensions[0] = length_x + dimensions[1] = length_y + + else: + # we are 2D, extended along y and z + # by default extend x to match y unless extend_dims is passed + length_y = self.ds.domain_dimensions[1] + length_x = length_y + if extend_dims: + length_x = extend_dims + + for f in data.keys(): + df, df_units = data[f] + data_3D[f] = (np.tile(df, (length_x, 1, 1)), df_units) + + left_edge = self.ds.domain_left_edge + left_edge[0] = left_edge[1] + right_edge = self.ds.domain_right_edge + right_edge[0] = right_edge[1] + dimensions = self.ds.domain_dimensions + dimensions[0] = length_x + + # return a new EmuDataset object + new_dataset = EmuDataset() + new_dataset.init_from_data(data_3D, left_edge=left_edge, right_edge=right_edge, sim_time=self.ds.current_time.in_units("s"), + dimensions=dimensions, length_unit=self.ds.length_unit, + periodicity=self.ds.periodicity, nprocs=1) + return new_dataset + diff --git a/Scripts/data_reduction/glass_viz_extract.py b/Scripts/data_reduction/glass_viz_extract.py new file mode 100644 index 00000000..7e3bef94 --- /dev/null +++ b/Scripts/data_reduction/glass_viz_extract.py @@ -0,0 +1,28 @@ +# script to extract data cube to give to Bathsheba Grossman (crystalproteins.com) + +import h5py +import yt +import os +import sys +sys.path.append(os.path.dirname(os.path.abspath(__file__))) +import emu_yt_module as emu +import numpy as np + +dirname = "plt02200" +component = "N01" +suffix = "" + +eds = emu.EmuDataset(dirname) +t = eds.ds.current_time + +NR = eds.cg[component+"_Re"+suffix] +NI = eds.cg[component+"_Im"+suffix] + +phi = np.arctan2(NI,NR) +print(np.min(phi), np.max(phi)) +print(np.shape(phi)) + +f = h5py.File(dirname+"_glass.h5","w") +f.create_dataset("phi",data=phi, dtype='float32') +#f["phi"] = phi +f.close() diff --git a/Scripts/data_reduction/kmax_t.py b/Scripts/data_reduction/kmax_t.py new file mode 100755 index 00000000..14288400 --- /dev/null +++ b/Scripts/data_reduction/kmax_t.py @@ -0,0 +1,62 @@ +# Create a file called kmax_t.dat that contains the amplitude, phase, and wavenumber of the biggest Fourier mode on the domain at every point in time. + +import numpy as np +import emu_yt_module as emu +import glob +import scipy +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("-o", "--output", type=str, default="kmax_t", help="Name of the output file") +parser.add_argument("-d", "--dataset", type=str, default="N01", help="Name of the dataset in which to find kmax. Omit _Re and _Im, as they will be added as needed.") +args = parser.parse_args() + +directories = sorted(glob.glob("plt?????")) + +# Need to know if the chosen dataset is flavor diagonal +# If so, we have to assume all imaginary values are zero +isDiagonal = (args.dataset[-1]==args.dataset[-2]) + +################################ +# read data and calculate FFTs # +################################ +f = open(args.output+"_"+args.dataset+".dat","w") +f.write("1:t(s)\t2:kx(1|cm,with2pi)\t3:ky(1|cm,with2pi)\t4:kz(1|cm,with2pi)\t5:magnitude(1|ccm)\t6:phase\n") +for d in directories: + print(d) + eds = emu.EmuDataset(d) + NF = eds.get_num_flavors() + + if isDiagonal: + fft = eds.fourier(args.dataset+"_Re") + else: + fft = eds.fourier(args.dataset+"_Re",args.dataset+"_Im") + + f.write(str(float(fft.time))+"\t") + + maxloc = np.unravel_index(np.argmax(fft.magnitude), fft.magnitude.shape) + + index = 0 + if fft.kx is not None: + f.write(str(fft.kx[maxloc[index]]*2.*np.pi)+"\t") + index += 1 + else: + f.write(str(0)+"\t") + + if fft.ky is not None: + f.write(str(fft.ky[maxloc[index]]*2.*np.pi)+"\t") + index += 1 + else: + f.write(str(0)+"\t") + + if fft.kz is not None: + f.write(str(fft.kz[maxloc[index]]*2.*np.pi)+"\t") + index += 1 + else: + f.write(str(0)+"\t") + + f.write(str(fft.magnitude[maxloc])+"\t") + f.write(str(fft.phase[maxloc])+"\n") + print(np.max(fft.magnitude), fft.magnitude[maxloc]) + +f.close() diff --git a/Scripts/data_reduction/reduce_data.py b/Scripts/data_reduction/reduce_data.py new file mode 100644 index 00000000..3b4fbbe2 --- /dev/null +++ b/Scripts/data_reduction/reduce_data.py @@ -0,0 +1,609 @@ +# used to make plots but now just generates a hdf5 file with domain-averaged data. +# Run in the directory of the simulation the data should be generated for. +# Still has functionality for per-snapshot plots, but the line is commented out. +# This version averages the magnitudes of off-diagonal components rather than the real/imaginary parts +# also normalizes fluxes by sumtrace of N rather than F. +# This data is used for the growth plot. +# Note - also tried a version using maxima rather than averages, and it did not make the growth plot look any better. + +import os +os.environ['HDF5_USE_FILE_LOCKING'] = 'FALSE' +import sys +sys.path.append(os.path.dirname(os.path.abspath(__file__))) +import numpy as np +import glob +import multiprocessing as mp +import h5py +import amrex_plot_tools as amrex +import emu_yt_module as emu +from multiprocessing import Pool +import scipy.special + +def dataset_name(moment, nu_nubar, i, j, ReIm): + data_format = format_dict["data_format"] + # make sure the inputs make sense + assert(i>=0) + assert(j>=0) + assert(i<=j) + if(i==j): + assert(ReIm=="Re") + assert(ReIm=="Re" or ReIm=="Im") + assert(moment=="N" or moment=="Fx" or moment=="Fy" or moment=="Fz") + assert(nu_nubar=="" or nu_nubar=="bar") + + # Emu + if(data_format=="Emu"): + return moment+str(i)+str(j)+"_"+ReIm+nu_nubar + + # FLASH + if(data_format=="FLASH"): + # make sure inputs only assume two flavors + assert(i<=1) + assert(j<=1) + + if moment=="N": momentFlash = "e" + if moment=="Fx": momentFlash = "f" + if moment=="Fy": momentFlash = "g" + if moment=="Fz": momentFlash = "h" + + if i==0 and j==0: + if nu_nubar=="": componentFlash = "e" + else: componentFlash = "a" + if j==1 and j==1: + if nu_nubar=="": componentFlash = "m" + else: componentFlash = "n" + if i==0 and j==1: + if ReIm=="Re": + if nu_nubar=="": componentFlash = "r" + else: componentFlash = "s" + else: + if nu_nubar=="": componentFlash = "i" + else: componentFlash = "j" + + return momentFlash+componentFlash+energyGroup + +# N is the corresponding flavor's number density (already converted to the proper units) +def convert_F_to_inv_ccm(N, data_format): + if(data_format=="Emu"): return 1.0 + if(data_format=="FLASH"): return N + + +##################### +# FFT preliminaries # +##################### +def get_kmid(fft): + if fft.kx is not None: + kmid = fft.kx[np.where(fft.kx>=0)] + if fft.ky is not None: + kmid = fft.ky[np.where(fft.ky>=0)] + if fft.kz is not None: + kmid = fft.kz[np.where(fft.kz>=0)] + return kmid + +def fft_coefficients(fft): + # add another point to the end of the k grid for interpolation + # MAKES POWER SPECTRUM HAVE SIZE ONE LARGER THAN KTEMPLATE + kmid = get_kmid(fft) + dk = kmid[1]-kmid[0] + kmid = np.append(kmid, kmid[-1]+dk) + + # compute the magnitude of the wavevector for every point + kmag = 0 + if fft.kx is not None: + kmag = kmag + fft.kx[:,np.newaxis,np.newaxis]**2 + if fft.ky is not None: + kmag = kmag + fft.ky[np.newaxis,:,np.newaxis]**2 + if fft.kz is not None: + kmag = kmag + fft.kz[np.newaxis,np.newaxis,:]**2 + kmag = np.sqrt(np.squeeze(kmag)) + kmag[np.where(kmag>=kmid[-1])] = kmid[-1] + + + # compute left index for interpolation + ileft = (kmag/dk).astype(int) + iright = ileft+1 + iright[np.where(iright>=len(kmid)-1)] = len(kmid)-1 + + # compute the fraction of the power that goes toward the left and right k point + cleft = (kmid[iright]-kmag)/dk + cright = 1.0-cleft + + return cleft, cright, ileft, iright, kmid + +def fft_power(fft, cleft, cright, ileft, iright, kmid): + + # compute power contributions to left and right indices + power = fft.magnitude**2 + powerLeft = power*cleft + powerRight = power*cright + + # accumulate onto spectrum + spectrum = np.array( [ + np.sum( powerLeft*(ileft ==i) + powerRight*(iright==i) ) + for i in range(len(kmid))] ) + + return spectrum + +######################### +# average preliminaries # +######################### +def get_matrix(ad,moment,nu_nubar): + NF = format_dict["NF"] + yt_descriptor = format_dict["yt_descriptor"] + f00 = ad[yt_descriptor, dataset_name(moment, nu_nubar, 0, 0, "Re")] + f01 = ad[yt_descriptor, dataset_name(moment, nu_nubar, 0, 1, "Re")] + f01I = ad[yt_descriptor, dataset_name(moment, nu_nubar, 0, 1, "Im")] + f11 = ad[yt_descriptor, dataset_name(moment, nu_nubar, 1, 1, "Re")] + if(NF>=3): + f02 = ad[yt_descriptor,dataset_name(moment, nu_nubar, 0, 2, "Re")] + f02I = ad[yt_descriptor,dataset_name(moment, nu_nubar, 0, 2, "Im")] + f12 = ad[yt_descriptor,dataset_name(moment, nu_nubar, 1, 2, "Re")] + f12I = ad[yt_descriptor,dataset_name(moment, nu_nubar, 1, 2, "Im")] + f22 = ad[yt_descriptor,dataset_name(moment, nu_nubar, 2, 2, "Re")] + zero = np.zeros(np.shape(f00)) + if(NF==2): + fR = [[f00 , f01 ], [ f01 ,f11 ]] + fI = [[zero, f01I], [-f01I,zero]] + if(NF==3): + fR = [[f00 , f01 , f02 ], [ f01 ,f11 ,f12 ], [ f02 , f12 ,f22 ]] + fI = [[zero, f01I, f02I], [-f01I,zero,f12I], [-f02I,-f12I,zero]] + return fR, fI + +def averaged_N(N, NI): + NF = format_dict["NF"] + R=0 + I=1 + + # do the averaging + # f1, f2, R/I + Nout = np.zeros((NF,NF)) + for i in range(NF): + for j in range(NF): + Nout[i][j] = float(np.average(np.sqrt(N[i][j]**2 + NI[i][j]**2))) + return np.array(Nout) + +def averaged_F(F, FI): + NF = format_dict["NF"] + R=0 + I=1 + + # do the averaging + # direction, f1, f2, R/I + Fout = np.zeros((3,NF,NF)) + for i in range(3): + for j in range(NF): + for k in range(NF): + Fout[i][j][k] = float(np.average(np.sqrt( F[i][j][k]**2 + FI[i][j][k]**2))) + + return Fout + +def offdiagMag(f): + NF = format_dict["NF"] + R = 0 + I = 1 + result = 0 + for f0 in range(NF): + for f1 in range(f0,NF): + result += f[:,f0,f1,R]**2 + f[:,f0,f1,I]**2 + return np.sqrt(result) + + + +class GridData(object): + def __init__(self, ad): + x = ad['index','x'].d + y = ad['index','y'].d + z = ad['index','z'].d + dx = ad['index','dx'].d + dy = ad['index','dy'].d + dz = ad['index','dz'].d + self.ad = ad + self.dx = dx[0] + self.dy = dy[0] + self.dz = dz[0] + self.xmin = np.min(x-dx/2.) + self.ymin = np.min(y-dy/2.) + self.zmin = np.min(z-dz/2.) + self.xmax = np.max(x+dx/2.) + self.ymax = np.max(y+dy/2.) + self.zmax = np.max(z+dz/2.) + self.nx = int((self.xmax - self.xmin) / self.dx + 0.5) + self.ny = int((self.ymax - self.ymin) / self.dy + 0.5) + self.nz = int((self.zmax - self.zmin) / self.dz + 0.5) + + + # particle cell id ON THE CURRENT GRID + # the x, y, and z values are assumed to be relative to the + # lower boundary of the grid + def get_particle_cell_ids(self,rdata): + # get coordinates + x = rdata[:,rkey["x"]] + y = rdata[:,rkey["y"]] + z = rdata[:,rkey["z"]] + ix = (x/self.dx).astype(int) + iy = (y/self.dy).astype(int) + iz = (z/self.dz).astype(int) + + # HACK - get this grid's bounds using particle locations + ix -= np.min(ix) + iy -= np.min(iy) + iz -= np.min(iz) + nx = np.max(ix)+1 + ny = np.max(iy)+1 + nz = np.max(iz)+1 + idlist = (iz + nz*iy + nz*ny*ix).astype(int) + return idlist + +# get the number of particles per cell +def get_nppc(d): + eds = emu.EmuDataset(d) + t = eds.ds.current_time + ad = eds.ds.all_data() + grid_data = GridData(ad) + level = 0 + gridID = 0 + idata, rdata = amrex.read_particle_data(d, ptype="neutrinos", level_gridID=(level,gridID)) + idlist = grid_data.get_particle_cell_ids(rdata) + ncells = np.max(idlist)+1 + nppc = len(idlist) // ncells + return nppc + + +# input list of particle data separated into grid cells +# output the same array, but sorted by zenith angle, then azimuthal angle +# also output the grid of directions in each cell (assumed to be the same) +def sort_rdata_chunk(p): + # sort first in theta + sorted_indices = p[:,rkey["pupz"]].argsort() + p = p[sorted_indices,:] + + # loop over unique values of theta + costheta = p[:,rkey["pupz"]] / p[:,rkey["pupt"]] + for unique_costheta in np.unique(costheta): + # get the array of particles with the same costheta + costheta_locs = np.where(costheta == unique_costheta)[0] + p_theta = p[costheta_locs,:] + + # sort these particles by the azimuthal angle + phi = np.arctan2(p_theta[:,rkey["pupy"]] , p_theta[:,rkey["pupx"]] ) + sorted_indices = phi.argsort() + p_theta = p_theta[sorted_indices,:] + + # put the sorted data back into p + p[costheta_locs,:] = p_theta + + # return the sorted array + return p + + +# class containing the set of coefficients to be multiplied by particle quantities +# compute once and use many times +class DiscreteSphericalHarmonic: + global Ylm_star_shared + + @staticmethod + def Ylm_indices(l): + start = l**2 + stop = (l+1)**2 + return start,stop + + # phat is the array of neutrino direction unit vectors in a single grid cell [particle, xyz] + # nl is the number of spherical harmonics to evaluate + def __init__(self, nl, nppc): + self.nl = nl + self.nppc = nppc + + # create shared object + # double the size for real+imaginary parts + global Ylm_star_shared + Ylm_star_shared = mp.RawArray('d', int(2 * (self.nl+1)**2 * self.nppc)) + + def precompute(self, phat): + # create local arrays that use this memory for easy modification + Ylm_star = np.frombuffer(Ylm_star_shared, dtype='complex').reshape( ( (self.nl+1)**2, self.nppc) ) + + # get direction coordinates + theta = np.arccos(phat[:,2]) + phi = np.arctan2(phat[:,1],phat[:,0]) + + # evaluate spherical harmonic amplitudes + for l in range(self.nl): + start,stop = self.Ylm_indices(l) + nm = stop-start + mlist = np.array(range(nm))-l + Ylm_star_thisl = [np.conj(scipy.special.sph_harm(m, l, phi, theta)) for m in mlist] + Ylm_star[start:stop] = np.array( Ylm_star_thisl ) + + def get_shared_Ylm_star(self,l): + assert(l0 + if do_average and not already_done: + thisN, thisNI = get_matrix(ad,"N","" ) + N = averaged_N(thisN,thisNI) * convert_N_to_inv_ccm + + thisFx, thisFxI = get_matrix(ad,"Fx","") + thisFy, thisFyI = get_matrix(ad,"Fy","") + thisFz, thisFzI = get_matrix(ad,"Fz","") + Ftmp = np.array([thisFx , thisFy , thisFz ]) + FtmpI = np.array([thisFxI, thisFyI, thisFzI]) + F = averaged_F(Ftmp, FtmpI) * convert_F_to_inv_ccm(N,data_format) + + thisN, thisNI = get_matrix(ad,"N","bar") + Nbar = averaged_N(thisN,thisNI) * convert_N_to_inv_ccm + + thisFx, thisFxI = get_matrix(ad,"Fx","bar") + thisFy, thisFyI = get_matrix(ad,"Fy","bar") + thisFz, thisFzI = get_matrix(ad,"Fz","bar") + Ftmp = np.array([thisFx , thisFy , thisFz ]) + FtmpI = np.array([thisFxI, thisFyI, thisFzI]) + Fbar = averaged_F(Ftmp, FtmpI) * convert_F_to_inv_ccm(Nbar,data_format) + + print("# rank",mpi_rank,"writing",outputfilename) + avgData = h5py.File(outputfilename,"w") + avgData["N_avg_mag(1|ccm)"] = [N,] + avgData["Nbar_avg_mag(1|ccm)"] = [Nbar,] + avgData["F_avg_mag(1|ccm)"] = [F,] + avgData["Fbar_avg_mag(1|ccm)"] = [Fbar,] + avgData["t(s)"] = [t,] + avgData.close() + + ############ + # FFT work # + ############ + outputfilename = d+"_reduced_data_fft_power.h5" + already_done = len(glob.glob(outputfilename))>0 + if do_fft and not already_done and len(eds.cg["N00_Re"][:,:,:].d.flatten())>1: + + print("# rank",mpi_rank,"writing",outputfilename) + fout = h5py.File(outputfilename,"w") + fout["t(s)"] = [np.array(t),] + + fft = eds.fourier(dataset_name("N", "", 0, 0, "Re"),nproc=nproc) + fout["k(1|cm)"] = get_kmid(fft) + cleft, cright, ileft, iright, kmid = fft_coefficients(fft) + N00_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fft = eds.fourier(dataset_name("N", "", 1, 1, "Re"),nproc=nproc) + N11_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fft = eds.fourier(dataset_name("N", "", 0, 1, "Re"), + dataset_name("N", "", 0, 1, "Im"),nproc=nproc) + N01_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fout["N00_FFT(cm^-2)"] = [np.array(N00_FFT),] + fout["N11_FFT(cm^-2)"] = [np.array(N11_FFT),] + fout["N01_FFT(cm^-2)"] = [np.array(N01_FFT),] + if format_dict["NF"]>2: + fft = eds.fourier(dataset_name("N", "", 2, 2, "Re"),nproc=nproc) + N22_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fft = eds.fourier(dataset_name("N", "", 0, 2, "Re"), + dataset_name("N", "", 0, 2, "Im"),nproc=nproc) + N02_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fft = eds.fourier(dataset_name("N", "", 1, 2, "Re"), + dataset_name("N", "", 1, 2, "Im"),nproc=nproc) + N12_FFT = fft_power(fft, cleft, cright, ileft, iright, kmid) + fout["N22_FFT(cm^-2)"] = [np.array(N22_FFT),] + fout["N02_FFT(cm^-2)"] = [np.array(N02_FFT),] + fout["N12_FFT(cm^-2)"] = [np.array(N12_FFT),] + + fout.close() + + if do_angular: + # separate loop for angular spectra so there is no aliasing and better load balancing + directories = sorted(glob.glob("plt*/neutrinos")) + directories = [directories[i].split('/')[0] for i in range(len(directories))] # remove "neutrinos" + + # get number of particles to be able to construct + # must build Ylm before pool or the internal shared memory will not be declared in pool subprocesses + nppc = get_nppc(directories[-1]) + Ylm = DiscreteSphericalHarmonic(nl,nppc) + + pool = Pool(nproc) + for d in directories: + if mpi_rank==0: + print("# working on", d) + eds = emu.EmuDataset(d) + t = eds.ds.current_time + ad = eds.ds.all_data() + + ################ + # angular work # + ################ + outputfilename = d+"_reduced_data_angular_power_spectrum.h5" + already_done = len(glob.glob(outputfilename))>0 + if not already_done: + + if mpi_rank==0: + print("Computing up to l =",nl-1) + + header = amrex.AMReXParticleHeader(d+"/neutrinos/Header") + grid_data = GridData(ad) + nlevels = len(header.grids) + assert nlevels==1 + level = 0 + ngrids = len(header.grids[level]) + + # average the angular power spectrum over many cells + # loop over all cells within each grid + if format_dict["NF"]==2: + ncomps = 3 + if format_dict["NF"]==3: + ncomps = 6 + spectrum = np.zeros((nl,2,ncomps)) + Nrho_avg = np.zeros((2,ncomps,nppc))*1j + total_ncells = 0 + for gridID in range(mpi_rank,ngrids,mpi_size): + print(" rank",mpi_rank,"grid",gridID+1,"/",ngrids) + + # read particle data on a single grid + # [particleID, quantity] + idata, rdata = amrex.read_particle_data(d, ptype="neutrinos", level_gridID=(level,gridID)) + + # get list of cell ids + idlist = grid_data.get_particle_cell_ids(rdata) + + # sort rdata based on id list + # still [particleID, quantity] + sorted_indices = idlist.argsort() + rdata = rdata[sorted_indices] + idlist = idlist[sorted_indices] + + # split up the data into cell chunks + # [cellID][particleID, quantity] + ncells = np.max(idlist)+1 + rdata = [ rdata[icell*nppc:(icell+1)*nppc,:] for icell in range(ncells)] # + chunksize = ncells//nproc + if ncells % nproc != 0: + chunksize += 1 + + # sort particles in each chunk + # still [cellID][particleID, quantity] + rdata = pool.map(sort_rdata_chunk, rdata, chunksize=chunksize) + + # initialize the shared data class. + #Only need to compute for one grid cell, since the angular grid is the same in every cell. + if gridID==mpi_rank: + phat = rdata[0][:,rkey["pupx"]:rkey["pupz"]+1] / rdata[0][:,rkey["pupt"]][:,np.newaxis] + Ylm.precompute(phat) + + # accumulate the spatial average of the angular distribution + Nrho = pool.map(get_Nrho,rdata, chunksize=chunksize) + Nrho_avg += sum(Nrho) + + # accumulate a spectrum from each cell + spectrum_each_cell = pool.map(Ylm.spherical_harmonic_power_spectrum, Nrho, chunksize=chunksize) + spectrum += sum(spectrum_each_cell) + + # count the total number of cells + total_ncells += ncells + + if do_MPI: + comm.Barrier() + spectrum = comm.reduce(spectrum , op=MPI.SUM, root=0) + Nrho_avg = comm.reduce(Nrho_avg , op=MPI.SUM, root=0) + total_ncells = comm.reduce(total_ncells, op=MPI.SUM, root=0) + + # write averaged data + if mpi_rank==0: + spectrum /= total_ncells + Nrho_avg /= total_ncells + + print("# writing",outputfilename) + avgData = h5py.File(outputfilename,"w") + avgData["angular_spectrum"] = [spectrum,] + avgData["Nrho(1|ccm)"] = [Nrho_avg,] + avgData["phat"] = phat + avgData["t(s)"] = [t,] + avgData.close() + + +if __name__ == "__main__": + reduce_data() diff --git a/Scripts/data_reduction/reduce_data_fft.py b/Scripts/data_reduction/reduce_data_fft.py new file mode 100755 index 00000000..15949757 --- /dev/null +++ b/Scripts/data_reduction/reduce_data_fft.py @@ -0,0 +1,149 @@ +import numpy as np +import emu_yt_module as emu +import h5py +import glob +import scipy +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("-o", "--output", type=str, default="reduced_data_fft.h5", help="Name of the output file (default: reduced_data_fft.h5)") +args = parser.parse_args() + +directories = glob.glob("plt*") +directories = [d for d in directories if ".h5" not in d] +directories = sorted(directories, key=lambda x: int(x.lstrip("plt"))) + +t = [] + +N00_FFT = [] +N01_FFT = [] +N02_FFT = [] +N11_FFT = [] +N12_FFT = [] +N22_FFT = [] + +Fx00_FFT = [] +Fx01_FFT = [] +Fx02_FFT = [] +Fx11_FFT = [] +Fx12_FFT = [] +Fx22_FFT = [] + +N00_FFT_phase = [] +N01_FFT_phase = [] +N02_FFT_phase = [] +N11_FFT_phase = [] +N12_FFT_phase = [] +N22_FFT_phase = [] + +Fx00_FFT_phase = [] +Fx01_FFT_phase = [] +Fx02_FFT_phase = [] +Fx11_FFT_phase = [] +Fx12_FFT_phase = [] +Fx22_FFT_phase = [] + +################################ +# read data and calculate FFTs # +################################ +for d in directories: + print(d) + eds = emu.EmuDataset(d) + NF = eds.get_num_flavors() + t.append(eds.ds.current_time) + + fft = eds.fourier("N00_Re") + N00_FFT.append(fft.magnitude) + N00_FFT_phase.append(fft.phase) + + fft = eds.fourier("N11_Re") + N11_FFT.append(fft.magnitude) + N11_FFT_phase.append(fft.phase) + + fft = eds.fourier("N01_Re","N01_Im") + N01_FFT.append(fft.magnitude) + N01_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx00_Re") + Fx00_FFT.append(fft.magnitude) + Fx00_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx11_Re") + Fx11_FFT.append(fft.magnitude) + Fx11_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx01_Re","Fx01_Im") + Fx01_FFT.append(fft.magnitude) + Fx01_FFT_phase.append(fft.phase) + + if NF>2: + fft = eds.fourier("N22_Re") + N22_FFT.append(fft.magnitude) + N22_FFT_phase.append(fft.phase) + + fft = eds.fourier("N02_Re","N02_Im") + N02_FFT.append(fft.magnitude) + N02_FFT_phase.append(fft.phase) + + fft = eds.fourier("N12_Re","N12_Im") + N12_FFT.append(fft.magnitude) + N12_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx22_Re") + Fx22_FFT.append(fft.magnitude) + Fx22_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx02_Re","Fx02_Im") + Fx02_FFT.append(fft.magnitude) + Fx02_FFT_phase.append(fft.phase) + + fft = eds.fourier("Fx12_Re","Fx12_Im") + Fx12_FFT.append(fft.magnitude) + Fx12_FFT_phase.append(fft.phase) + + +################## +# write the file # +################## +f = h5py.File(args.output,"w") + +f["t"] = np.array(t) + +if fft.kx is not None: + f["kx"] = np.array(fft.kx) +if fft.ky is not None: + f["ky"] = np.array(fft.ky) +if fft.kz is not None: + f["kz"] = np.array(fft.kz) + +f["N00_FFT"] = np.array(N00_FFT) +f["N11_FFT"] = np.array(N11_FFT) +f["N01_FFT"] = np.array(N01_FFT) +f["Fx00_FFT"] = np.array(Fx00_FFT) +f["Fx11_FFT"] = np.array(Fx11_FFT) +f["Fx01_FFT"] = np.array(Fx01_FFT) + +f["N00_FFT_phase"] = np.array(N00_FFT_phase) +f["N11_FFT_phase"] = np.array(N11_FFT_phase) +f["N01_FFT_phase"] = np.array(N01_FFT_phase) +f["Fx00_FFT_phase"] = np.array(Fx00_FFT_phase) +f["Fx11_FFT_phase"] = np.array(Fx11_FFT_phase) +f["Fx01_FFT_phase"] = np.array(Fx01_FFT_phase) + +if NF>2: + f["N22_FFT"] = np.array(N22_FFT) + f["N02_FFT"] = np.array(N02_FFT) + f["N12_FFT"] = np.array(N12_FFT) + f["Fx22_FFT"] = np.array(Fx22_FFT) + f["Fx02_FFT"] = np.array(Fx02_FFT) + f["Fx12_FFT"] = np.array(Fx12_FFT) + + f["N22_FFT_phase"] = np.array(N22_FFT_phase) + f["N02_FFT_phase"] = np.array(N02_FFT_phase) + f["N12_FFT_phase"] = np.array(N12_FFT_phase) + f["Fx22_FFT_phase"] = np.array(Fx22_FFT_phase) + f["Fx02_FFT_phase"] = np.array(Fx02_FFT_phase) + f["Fx12_FFT_phase"] = np.array(Fx12_FFT_phase) + + +f.close() diff --git a/Scripts/data_reduction/submit_reduce_data.sh b/Scripts/data_reduction/submit_reduce_data.sh new file mode 100644 index 00000000..d42b7b5d --- /dev/null +++ b/Scripts/data_reduction/submit_reduce_data.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Number of nodes: +#SBATCH --nodes=8 +# +################# +# Haswell nodes +################# +# +# Requests Cori Haswell nodes: +#SBATCH --constraint=haswell +# +# Haswell: Assign 1 MPI task to each socket +#SBATCH --tasks-per-node=16 +# +# Haswell: each socket has 32 CPUs (with hyperthreading) +#SBATCH --cpus-per-task=4 +# +################# +# Queue & Job +################# +# +# Which queue to run in: debug, regular, premium, etc. ... +#SBATCH --qos=regular +# +# Run for this much walltime: hh:mm:ss +#SBATCH --time=24:00:00 +# +# Use this job name: +#SBATCH -J emu_reduce +# +# Send notification emails here: +#SBATCH --mail-user=srichers@berkeley.edu +#SBATCH --mail-type=ALL +# +# Which allocation to use: +#SBATCH -A m3761 + +# On the compute node, change to the directory we submitted from +cd $SLURM_SUBMIT_DIR + +module load python3 + +srun -n 128 -c 4 python3 ~/emu_scripts/data_reduction/reduce_data.py diff --git a/Scripts/initial_conditions/.gitignore b/Scripts/initial_conditions/.gitignore new file mode 100644 index 00000000..8025bc04 --- /dev/null +++ b/Scripts/initial_conditions/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*~ diff --git a/Scripts/initial_conditions/draw_eln.py b/Scripts/initial_conditions/draw_eln.py new file mode 100644 index 00000000..e153cab4 --- /dev/null +++ b/Scripts/initial_conditions/draw_eln.py @@ -0,0 +1,99 @@ +import matplotlib.pyplot as plt +import matplotlib as mpl +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_reduction") +from initial_condition_tools import uniform_sphere, moment_interpolate_particles, minerbo_interpolate, write_particles +import amrex_plot_tools as amrex + +NF = 2 +nphi_equator = 1024 +nnue = 1.421954234999705e+33 +nnua = 1.9146237131657563e+33 +nnux = 1.9645407875568215e+33 +fnue = np.array([0.0974572, 0.04217632, -0.13433261]) +fnua = np.array([0.07237959, 0.03132354, -0.3446878]) +fnux = np.array([-0.02165833, 0.07431613, -0.53545951]) +energy_erg = 20.05473294163565 * 1e6*amrex.eV + +nnu = np.zeros((2,NF)) +nnu[0,0] = nnue +nnu[1,0] = nnua +nnu[:,1:] = nnux / 4. + +fnu = np.zeros((2,NF,3)) +fnu[0,0,:] = nnue * fnue +fnu[1,0,:] = nnua * fnua +fnu[:,1:,:] = nnu[:,1:,np.newaxis] * fnux[np.newaxis,np.newaxis,:] + +particles = moment_interpolate_particles(nphi_equator, nnu, fnu, energy_erg, uniform_sphere, minerbo_interpolate) # [particle, variable] + +rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + +pxhat = particles[:,rkey["pupx"]] / particles[:,rkey["pupt"]] +pyhat = particles[:,rkey["pupy"]] / particles[:,rkey["pupt"]] +pzhat = particles[:,rkey["pupz"]] / particles[:,rkey["pupt"]] + +peq = np.sqrt(pxhat**2+pyhat**2) +pmag = np.sqrt(pxhat**2+pyhat**2+pzhat**2) + +mu = pzhat / pmag +phi = np.arctan2(pxhat,pyhat) + +N = particles[:,rkey["N"]] +Nbar = particles[:,rkey["Nbar"]] +f00 = particles[:,rkey["f00_Re"]] +f00bar = particles[:,rkey["f00_Rebar"]] + +eln = N*f00 - Nbar*f00bar + +#==============# +# plot options # +#==============# +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +mpl.rc('text', usetex=True) +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 +mpl.rcParams['axes.linewidth'] = 2 + +#==========# +# subplots # +#==========# +fig,axes=plt.subplots(1,1, figsize=(8,6)) +plt.subplots_adjust(wspace=0, hspace=0) + +elnmax = np.max(np.abs(eln)) + +sc0 = axes.scatter(phi, mu, c=eln, cmap=mpl.cm.seismic, s=3, vmin=-elnmax, vmax=elnmax) + +particle_direction = np.array([3.03e-6, 2.718e-5, -1.687e-5]) +particle_mag = np.sqrt(np.sum(particle_direction**2)) +particle_mu = particle_direction[2] / particle_mag +particle_phi = np.arctan2(particle_direction[0], particle_direction[1]) +print(particle_mag) +axes.scatter(particle_phi, particle_mu, c='green', s=3) + +particle_direction = np.array([-1.321e-5, -6.981e-6, 2.840e-5]) +particle_mag = np.sqrt(np.sum(particle_direction**2)) +particle_mu = particle_direction[2] / particle_mag +particle_phi = np.arctan2(particle_direction[0], particle_direction[1]) +print(particle_mag) +axes.scatter(particle_phi, particle_mu, c='black', s=3) +#plt.colorbar() + +axes.set_xlabel(r"$\phi$") +axes.set_ylabel(r"$\mu$") + +plt.savefig("eln.pdf",bbox_inches="tight") diff --git a/Scripts/initial_conditions/initial_condition_tools.py b/Scripts/initial_conditions/initial_condition_tools.py new file mode 100644 index 00000000..d0885bdd --- /dev/null +++ b/Scripts/initial_conditions/initial_condition_tools.py @@ -0,0 +1,401 @@ +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath+"/../data_reduction") +import amrex_plot_tools as amrex + +# generate an array of theta,phi pairs that uniformily cover the surface of a sphere +# based on DOI: 10.1080/10586458.2003.10504492 section 3.3 but specifying n_j=0 instead of n +def uniform_sphere(nphi_at_equator): + assert(nphi_at_equator > 0) + + dtheta = np.pi * np.sqrt(3) / nphi_at_equator + + xyz = [] + theta = 0 + phi0 = 0 + while(theta < np.pi/2): + nphi = nphi_at_equator if theta==0 else int(round(nphi_at_equator * np.cos(theta))) + dphi = 2*np.pi/nphi + if(nphi==1): theta = np.pi/2 + + for iphi in range(nphi): + phi = phi0 = iphi*dphi + x = np.cos(theta) * np.cos(phi) + y = np.cos(theta) * np.sin(phi) + z = np.sin(theta) + xyz.append(np.array([x,y,z])) + if(theta>0): xyz.append(np.array([-x,-y,-z])) + theta += dtheta + phi0 += 0.5 * dphi # offset by half step so adjacent latitudes are not always aligned in longitude + + return np.array(xyz) + + +# generate an array of theta,phi pairs that cover a sphere evenly in phi and cos(theta) +def grid_sphere(nphi): + assert(nphi > 0) + nmu = nphi // 2 + + mu_grid = np.linspace(-1,1,num=nmu+1) + costheta = np.array([ (mu_grid[i]+mu_grid[i+1])/2. for i in range(nmu)]) + sintheta = np.sqrt(1. - costheta**2) + + phi_mid = np.linspace(0, 2.*np.pi, num=nphi, endpoint=False) + cosphi = np.cos(phi_mid) + sinphi = np.sin(phi_mid) + + xyz = [] + for imu in range(nmu): + for iphi in range(nphi): + x = sintheta[imu] * cosphi[iphi] + y = sintheta[imu] * sinphi[iphi] + z = costheta[imu] + xyz.append(np.array([x,y,z])) + return np.array(xyz) + + +def write_particles(p, NF, filename): + with open(filename, "w") as f: + # write the number of flavors + f.write(str(NF)+"\n") + + for i in range(len(p)): + for j in range(len(p[i])): + f.write(str(p[i,j])+" ") + f.write("\n") + +# angular structure as determined by the Minerbo closure +# Z is a parameter determined by the flux factor +# mu is the cosine of the angle relative to the flux direction +# Coefficients set such that the expectation value is 1 +def minerbo_closure(Z, mu): + minfluxfac = 1e-3 + result = np.exp(Z*mu) + if(Z/3.0 > minfluxfac): + result *= Z/np.sinh(Z) + return result + + +# residual for the root finder +# Z needs to be bigger if residual is positive +# Minerbo (1978) (unfortunately under Elsevier paywall) +# Can also see Richers (2020) https://ui.adsabs.harvard.edu/abs/2020PhRvD.102h3017R +# Eq.41 (where a is Z), but in the non-degenerate limit +# k->0, eta->0, N->Z/(4pi sinh(Z)) (just to make it integrate to 1) +# minerbo_residual is the "f" equation between eq.42 and 43 +def minerbo_residual(fluxfac, Z): + return fluxfac - 1.0/np.tanh(Z) + 1.0 / Z + +def minerbo_residual_derivative(fluxfac, Z): + return 1.0/np.sinh(Z)**2 - 1.0/Z**2 + +def minerbo_Z(fluxfac): + # hard-code in these parameters because they are not + # really very important... + maxresidual = 1e-6 + maxcount = 20 + minfluxfac = 1e-3 + + # set the initial conditions + Z = 1.0 + + # catch the small flux factor case to prevent nans + if(fluxfac < minfluxfac): + Z = 3.*fluxfac + else: + residual = 1.0 + count = 0 + while(abs(residual)>maxresidual and countmaxresidual: + print("Failed to converge on a solution.") + assert(False) + + #print("fluxfac=",fluxfac," Z=",Z) + return Z + +# angular structure as determined by the Levermore closure +# from assuming that radiation is isotropic in some frame +# v is the velocity of this frame +# sign of v chosen so distribution is large when mu==1 +# Coefficients set such that the expectation value is 1 +def levermore_closure(v, mu): + gamma2 = 1/(1-v**2) + result = 1/(2*gamma2*(1-v*mu)**2) + return result + + +# residual for root finder to get v from the fluxfac +def levermore_residual(fluxfac, v): + return fluxfac - ( v-(1-v**2)*np.arctanh(v) ) / v**2 +def levermore_residual_derivative(fluxfac, v): + return 2*(v-np.arctanh(v))/v**3 + +def levermore_v(fluxfac): + # hard-code in these parameters because they are not + # really very important... + maxresidual = 1e-6 + maxcount = 20 + minfluxfac = 1e-3 + + # initial condition + v = 0.5 + + # catch the small flux factor case to prevent nans + if(fluxfac < minfluxfac): + v = 3*f/2 + else: + residual = 1.0 + count = 0 + while(abs(residual)>maxresidual and countmaxresidual: + print("Failed to converge on a solution.") + assert(False) + + #print("fluxfac=",fluxfac," v=",v) + return v + +# interpolate the levermore closure +# mu is the cosine of the angle of the direction relative to the flux direction +# the expectation value of the result is 1 +def levermore_interpolate(fluxfac, mu): + assert(fluxfac >= 0 and fluxfac <= 1) + + v = levermore_v(fluxfac) + return levermore_closure(v, mu) + +# interpolate the Minerbo closure +# mu is the cosine of the angle of the direction relative to the flux direction +# the expectation value of the result is 1 +def minerbo_interpolate(fluxfac, mu): + assert(fluxfac >= 0 and fluxfac <= 1) + + Z = minerbo_Z(fluxfac) + return minerbo_closure(Z, mu) + +# interpolate linearly +# mu is the cosine of the angle of the direction relative to the flux direction +# the expectation value of the result is 1 +def linear_interpolate(fluxfac, mu): + assert(fluxfac>=0 and fluxfac<=1/3) + return 1 + mu + +# generate a list of particle data +# NF is the number of flavors +# nphi_equator is the number of directions in the xhat-yhat plane +# nnu is an array of neutrino number density of lenth NF and units 1/ccm [nu/nubar, iflavor] +# fnu is an array of neutrino flux density of shape [nu/nubar, iflavor, xyz] and units of 1/ccm +# direction_generator is either uniform_sphere or grid_sphere +# interpolate_function is either levermore_interpolate or minerbo_interpolate or linear_interpolate +# for each nu/nubar and flavor, the interpolant adds up to 1 and approximates the flux +def moment_interpolate_particles(nphi_equator, nnu, fnu, energy_erg, direction_generator, interpolate_function): + # number of neutrino flavors + NF = nnu.shape[1] + + # flux magnitude and flux factor [nu/nubar, flavor] + fluxmag = np.sqrt(np.sum(fnu**2, axis=2)) + fluxfac = fluxmag / nnu + fluxfac[np.where(nnu==0)] = 0 + + # direction unit vector of fluxes [nu/nubar, flavor, xyz] + # point in arbitrary direction if fluxmag is zero + fhat = fnu / fluxmag[:,:,np.newaxis] + fhat[np.where(fhat!=fhat)] = 1./np.sqrt(3) + + # generate list of momenta and direction cosines + phat = direction_generator(nphi_equator) # [iparticle, xyz] + mu = np.sum(phat[:,np.newaxis,np.newaxis,:] * fhat[np.newaxis,:,:,:],axis=3) # [iparticle, nu/nubar, flavor] + + # generate interpolant # [particle, nu/nubar, flavor] + nparticles = mu.shape[0] + interpolant = np.array( [ [ interpolate_function(fluxfac[nu_nubar,flavor], mu[:,nu_nubar,flavor]) for nu_nubar in range(2)] for flavor in range(NF)] ) # [flavor, nu/nubar, particle] + interpolant = np.swapaxes(interpolant, 0, 2) + + # make sure the interpolant adds up to 1 + norm = np.sum(interpolant, axis=(0)) # [nu/nubar, flavor] + interpolant /= norm[np.newaxis,:,:] + + # determine the number of each flavor for each particle [particle, nu/nubar, flavor] + n_particle = interpolant * nnu[np.newaxis,:,:] + + # get variable keys + rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + nelements = len(rkey) + + # generate the list of particle info + particles = np.zeros((nparticles,nelements)) + + # save particle momenta + particles[:,rkey["pupt"] ] = energy_erg + particles[:,rkey["pupx"]:rkey["pupz"]+1] = energy_erg * phat + + # save the total number density of neutrinos for each particle + n_flavorsummed = np.sum(n_particle, axis=2) # [particle, nu/nubar] + for nu_nubar, suffix in zip(range(2), ["","bar"]): + for flavor in range(NF): + fvarname = "N"+str(flavor)+str(flavor)+"_Re"+suffix + particles[:,rkey[fvarname]] = n_particle[:,nu_nubar, flavor] + + return particles + +# generate a list of particle data, assuming each flavor's flux represents a single beam +# fnu is an array of neutrino flux density of shape [nu/nubar, iflavor, xyz] and units of 1/ccm +def beam_particles(fnu, energy_erg): + # number of neutrino flavors + NF = fnu.shape[1] + + # flux magnitude and flux factor [nu/nubar, flavor] + fluxmag = np.sqrt(np.sum(fnu**2, axis=2)) + + # direction unit vector of fluxes [nu/nubar, flavor, xyz] + # point in arbitrary direction if fluxmag is zero + fhat = fnu / fluxmag[:,:,np.newaxis] + fhat[np.where(fhat!=fhat)] = 1./np.sqrt(3) + + # generate interpolant # [particle, nu/nubar, flavor] + nparticles = 2*NF + + # get variable keys + rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + nelements = len(rkey) + + # generate the list of particle info + particles = np.zeros((nparticles,nelements)) + + # save the total number density of neutrinos for each particle + for flavor in range(NF): + fvarname = "f"+str(flavor)+str(flavor)+"_Re" + for nu_nubar, suffix in zip(range(2), ["","bar"]): + ind = nu_nubar*NF + flavor + + # set only the one flavor-flavor diagonal to 1 + # Also set an element for the anti-particle to be one so that density matrix still has unit trace, even though it has zero weight + particles[ind,rkey[fvarname ]] = 1 + particles[ind,rkey[fvarname+"bar"]] = 1 + + # set the number density to be equal to the magnitude of the flux + particles[ind,rkey["N"+suffix]] = fluxmag[nu_nubar,flavor] + + # set the direction to be equal to the direction of the flux + particles[ind,rkey["pupt"]] = energy_erg + particles[ind,rkey["pupx"]:rkey["pupz"]+1] = energy_erg * fhat[nu_nubar,flavor,:] + + return particles + +# create a random distribution defined by number and flux densities +# NF is the number of flavors +# n is normalized such that the sum of all ns is 1 +def random_moments(NF): + bad_distro = True + count = 0 + + # create random number densities, normalize to 1 + n = np.random.rand(2,NF) + n /= np.sum(n) + + # randomly sample flux directions + mu = np.random.rand(2,NF)*2. - 1 + phi = np.random.rand(2,NF)*2.*np.pi + mag = np.random.rand(2,NF) * n + + f = np.zeros((2,NF,3)) + sintheta = np.sqrt(1-mu**2) + f[:,:,0] = mag * sintheta * np.cos(phi) + f[:,:,1] = mag * sintheta * np.sin(phi) + f[:,:,2] = mag * mu + + return n, f + +# v has 3 components +# v is dimensionless velocity +def lorentz_transform(v): + v2 = np.sum(v**2) + gamma = 1/np.sqrt(1-v2) + + L = np.identity(4) + L[3,3] = gamma + L[0:3,3] = -gamma * v + L[3,0:3] = -gamma * v + L[0:3, 0:3] += (gamma-1) * np.outer(v,v) / v2 + + return L + +# returns a rotation matrix that rotates along axis u by angle costheta +def axis_angle_rotation_matrix(u, costheta): + costheta_2 = np.sqrt((1+costheta)/2.) + sintheta_2 = np.sqrt((1-costheta)/2.) + + # get rotation quaternion + q = np.array([costheta_2, + sintheta_2 * u[0], + sintheta_2 * u[1], + sintheta_2 * u[2] + ]) + + # construct rotation matrix ala wikipedia + R = np.zeros((4,4)) + for i in range(3): + # R[0,0] = q0^2 + q1^2 - q2^2 - q3^2 + # = costheta^2 + 2q1^2 - sintheta^2 (assuming u is a unit vector) + # Let the loop over j take into accout the 2q1^2 + R[i,i] = 2.*costheta_2**2 - 1. + + for j in range(3): + R[i,j] += 2.*q[i+1]*q[j+1] # indexing = q is size 4 + R[0,1] -= 2.*q[0]*q[2+1] + R[1,0] += 2.*q[0]*q[2+1] + R[0,2] += 2.*q[0]*q[1+1] + R[2,0] -= 2.*q[0]*q[1+1] + R[1,2] -= 2.*q[0]*q[0+1] + R[2,1] += 2.*q[0]*q[0+1] + + R[3,3] = 1 + + return R + +# return a matrix that transforms a set of four-fluxes +# such that the net flux is zero +# and the ELN points along the z axis +# n = [nu/nubar, flavor] +# f = [nu/nubar, flavor, xyz] +def poincare_transform_0flux_elnZ(n,f): + # number of flavors + NF = n.shape[1] + + # construct the four-flux of all flavors + f4 = np.zeros((2,NF,4)) + f4[:,:,0:3] = f + f4[:,:, 3] = n + f4 = np.moveaxis(f4, 2, 0) # [xyzt, nu/nubar, NF] + + # net flux factor + netf4 = np.sum(f4, axis=(1,2)) + average_velocity = netf4[0:3] / netf4[3] + L = lorentz_transform(average_velocity) + f4 = np.tensordot(L,f4, axes=1) + + # get the angle of rotation to the z axis + f4_flavorsum = np.sum(f4, axis=2) # [xyzt, nu/nubar] + f4_eln = f4_flavorsum[:,0] - f4_flavorsum[:,1] # [xyz] + fn_eln_mag = np.sqrt(np.sum(f4_eln[:3]**2)) + costheta = f4_eln[2] / fn_eln_mag + + # get axis of rotation (the axis normal to the e and a fluxes) + u = np.cross(f4_eln[:3], np.array([0,0,1]), axisa=0, axisc=0) + u /= np.sqrt(np.sum(u**2)) + + R = axis_angle_rotation_matrix(u, costheta) + f4 = np.tensordot(R,f4, axes=1) + + f4 = np.moveaxis(f4, 0, 2) # [nu/nubar, flavor, xyzt] + + return f4[:,:,3], f4[:,:,0:3] diff --git a/Scripts/initial_conditions/st0_msw_test.py b/Scripts/initial_conditions/st0_msw_test.py new file mode 100644 index 00000000..1c6767d5 --- /dev/null +++ b/Scripts/initial_conditions/st0_msw_test.py @@ -0,0 +1,43 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../visualization") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 2 +nphi_equator = 1 +ndens_per_particle = 1 # cm^-3 +m2 = 0 # erg +m1 = -0.008596511*amrex.eV/amrex.clight**2 # g +theta12 = 33.82 * np.pi/180 + +# set energy so that a vacuum oscillation wavelength occurs over a distance of 1cm +dm2 = (m2-m1)**2 #g^2 +energy_erg = dm2*amrex.clight**4 * np.sin(2.*theta12) / (8.*np.pi*amrex.hbar*amrex.clight) # *1cm for units + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF,ignore_pos=True) +nelements = len(rkey) + +# generate the grid of direction coordinates +phat = uniform_sphere(nphi_equator) +nparticles = len(phat) + +# generate the list of particle info +particles = np.zeros((nparticles,nelements)) +for ip in range(len(phat)): + p = particles[ip] + p[rkey["pupt"]] = energy_erg + p[rkey["pupx"]] = phat[ip,0] * energy_erg + p[rkey["pupy"]] = phat[ip,1] * energy_erg + p[rkey["pupz"]] = phat[ip,2] * energy_erg + p[rkey["N00_Re"]] = 1 + p[rkey["N00_Rebar"]] = 1 + +write_particles(np.array(particles), NF, "particle_input.dat") \ No newline at end of file diff --git a/Scripts/initial_conditions/st1_bipolar_test.py b/Scripts/initial_conditions/st1_bipolar_test.py new file mode 100644 index 00000000..4f8948c1 --- /dev/null +++ b/Scripts/initial_conditions/st1_bipolar_test.py @@ -0,0 +1,48 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../visualization") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 2 +nphi_equator = 1 +m2 = 0 # erg +m1 = -0.008596511*amrex.eV/amrex.clight**2 # g + +# generate the grid of direction coordinates +phat = uniform_sphere(nphi_equator) +nparticles = len(phat) + +# set particle weight such that density is +# 10 dm2 c^4 / (2 sqrt(2) GF E) +energy_erg = 50 * 1e6*amrex.eV +dm2 = (m2-m1)**2 #g^2 +print(dm2*amrex.clight**4/(2.*energy_erg),"erg") +# double omega = dm2*PhysConst::c4 / (2.*p.rdata(PIdx::pupt)); +ndens = 10. * dm2*amrex.clight**4 / (2.*np.sqrt(2.) * amrex.GF * energy_erg) +print(ndens,"cm^-3") +# double mu = sqrt(2.)*PhysConst::GF * ndens +ndens_per_particle = ndens / nparticles # cm^-3 + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF,ignore_pos=True) +nelements = len(rkey) + +# generate the list of particle info +particles = np.zeros((nparticles,nelements)) +for ip in range(len(phat)): + p = particles[ip] + p[rkey["pupt"]] = energy_erg + p[rkey["pupx"]] = phat[ip,0] * energy_erg + p[rkey["pupy"]] = phat[ip,1] * energy_erg + p[rkey["pupz"]] = phat[ip,2] * energy_erg + p[rkey["N00_Re"]] = ndens_per_particle + p[rkey["N00_Rebar"]] = ndens_per_particle + +write_particles(np.array(particles), NF, "particle_input.dat") \ No newline at end of file diff --git a/Scripts/initial_conditions/st2_2beam_fast_flavor.py b/Scripts/initial_conditions/st2_2beam_fast_flavor.py new file mode 100644 index 00000000..6839e9d5 --- /dev/null +++ b/Scripts/initial_conditions/st2_2beam_fast_flavor.py @@ -0,0 +1,49 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../visualization") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 2 +nphi_equator = 1 +m2 = 0 # erg +m1 = -0.008596511*amrex.eV/amrex.clight**2 # g + +# generate the grid of direction coordinates +phat = uniform_sphere(nphi_equator) +nparticles = len(phat) + +# set particle weight such that density is +# 0.5 dm2 c^4 / (2 sqrt(2) GF E) +# to get maximal growth according to Chakraborty 2016 Equation 2.10 +energy_erg = 50 * 1e6*amrex.eV +dm2 = (m2-m1)**2 #g^2 +omega = dm2*amrex.clight**4 / (2.* energy_erg) +mu_ndens = np.sqrt(2.) * amrex.GF # SI potential divided by the number density +ndens = omega / (2.*mu_ndens) # want omega/2mu to be 1 +ndens_per_particle = ndens / nparticles # cm^-3 + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF,ignore_pos=True) +nelements = len(rkey) + + +# generate the list of particle info +particles = np.zeros((nparticles,nelements)) +for ip in range(len(phat)): + u = phat[ip] + p = particles[ip] + p[rkey["pupt"]] = energy_erg + p[rkey["pupx"]] = u[0] * energy_erg + p[rkey["pupy"]] = u[1] * energy_erg + p[rkey["pupz"]] = u[2] * energy_erg + p[rkey["N00_Re"]] = ndens_per_particle * (1. + u[2]) + p[rkey["N00_Rebar"]] = ndens_per_particle * (1. - u[2]) + +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/initial_conditions/st3_2beam_fast_flavor_nonzerok.py b/Scripts/initial_conditions/st3_2beam_fast_flavor_nonzerok.py new file mode 100644 index 00000000..c2fd2da8 --- /dev/null +++ b/Scripts/initial_conditions/st3_2beam_fast_flavor_nonzerok.py @@ -0,0 +1,50 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../visualization") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 2 +nphi_equator = 1 +m2 = 0 # erg +m1 = -0.008596511*amrex.eV/amrex.clight**2 # g +perturbation_wavelength_cm = 1 + +# generate the grid of direction coordinates +phat = uniform_sphere(nphi_equator) +nparticles = len(phat) + +# set particle weight such that density is +# 0.5 dm2 c^4 / (2 sqrt(2) GF E) +# to get maximal growth according to Chakraborty 2016 Equation 2.10 +energy_erg = 50 * 1e6*amrex.eV +dm2 = (m2-m1)**2 #g^2 +omega = dm2*amrex.clight**4 / (2.* energy_erg) +mu_ndens = np.sqrt(2.) * amrex.GF # SI potential divided by the number density +nu_k = (2.*np.pi) / perturbation_wavelength_cm +ndens = (omega + nu_k*amrex.hbar*amrex.clight) / (2.*mu_ndens) # want omega/2mu to be 1 +ndens_per_particle = ndens / nparticles # cm^-3 + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF,ignore_pos=True) +nelements = len(rkey) + +# generate the list of particle info +particles = np.zeros((nparticles,nelements)) +for ip in range(len(phat)): + u = phat[ip] + p = particles[ip] + p[rkey["pupt"]] = energy_erg + p[rkey["pupx"]] = u[0] * energy_erg + p[rkey["pupy"]] = u[1] * energy_erg + p[rkey["pupz"]] = u[2] * energy_erg + p[rkey["N00_Re"]] = ndens_per_particle * (1. + u[2]) + p[rkey["N00_Rebar"]] = ndens_per_particle * (1. - u[2]) + +write_particles(np.array(particles), NF, "particle_input.dat") \ No newline at end of file diff --git a/Scripts/initial_conditions/st4_linear_moment_ffi.py b/Scripts/initial_conditions/st4_linear_moment_ffi.py new file mode 100644 index 00000000..661c5cfe --- /dev/null +++ b/Scripts/initial_conditions/st4_linear_moment_ffi.py @@ -0,0 +1,45 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_analysis") +from initial_condition_tools import uniform_sphere, write_particles, moment_interpolate_particles, linear_interpolate +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 2 +nphi_equator = 16 +theta = 0 +thetabar = 3.14159265359 +phi = 0 +phibar=0 +ndens = 4.891290819e+32 +ndensbar = 4.891290819e+32 +fluxfac = .333333333333333 +fluxfacbar = .333333333333333 +energy_erg = 50 * 1e6*amrex.eV + +# flux factor vectors +fhat = np.array([np.cos(phi) *np.sin(theta ), + np.sin(phi) *np.sin(theta ), + np.cos(theta )]) +fhatbar = np.array([np.cos(phibar)*np.sin(thetabar), + np.sin(phibar)*np.sin(thetabar), + np.cos(thetabar)]) + +nnu = np.zeros((2,NF)) +nnu[0,0] = ndens +nnu[1,0] = ndensbar +nnu[:,1:] = 0 + +fnu = np.zeros((2,NF,3)) +fnu[0,0,:] = ndens * fluxfac * fhat +fnu[1,0,:] = ndensbar * fluxfacbar * fhatbar +fnu[:,1:,:] = 0 + +particles = moment_interpolate_particles(nphi_equator, nnu, fnu, energy_erg, uniform_sphere, linear_interpolate) # [particle, variable] + +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/initial_conditions/st4_linear_moment_ffi_3F.py b/Scripts/initial_conditions/st4_linear_moment_ffi_3F.py new file mode 100644 index 00000000..93bdc96c --- /dev/null +++ b/Scripts/initial_conditions/st4_linear_moment_ffi_3F.py @@ -0,0 +1,45 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_analysis") +from initial_condition_tools import uniform_sphere, write_particles, moment_interpolate_particles, linear_interpolate +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +NF = 3 +nphi_equator = 16 +theta = 0 +thetabar = 3.14159265359 +phi = 0 +phibar=0 +ndens = 4.891290819e+32 +ndensbar = 4.891290819e+32 +fluxfac = .333333333333333 +fluxfacbar = .333333333333333 +energy_erg = 50 * 1e6*amrex.eV + +# flux factor vectors +fhat = np.array([np.cos(phi) *np.sin(theta ), + np.sin(phi) *np.sin(theta ), + np.cos(theta )]) +fhatbar = np.array([np.cos(phibar)*np.sin(thetabar), + np.sin(phibar)*np.sin(thetabar), + np.cos(thetabar)]) + +nnu = np.zeros((2,NF)) +nnu[0,0] = ndens +nnu[1,0] = ndensbar +nnu[:,1:] = 0 + +fnu = np.zeros((2,NF,3)) +fnu[0,0,:] = ndens * fluxfac * fhat +fnu[1,0,:] = ndensbar * fluxfacbar * fhatbar +fnu[:,1:,:] = 0 + +particles = moment_interpolate_particles(nphi_equator, nnu, fnu, energy_erg, uniform_sphere, linear_interpolate) # [particle, variable] + +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/initial_conditions/st5_minerbo.py b/Scripts/initial_conditions/st5_minerbo.py new file mode 100644 index 00000000..292b5e65 --- /dev/null +++ b/Scripts/initial_conditions/st5_minerbo.py @@ -0,0 +1,35 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_reduction") +from initial_condition_tools import uniform_sphere, moment_interpolate_particles, minerbo_interpolate, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# from ix=37 iy=86 iz=75 in Francois' data +NF = 2 +nphi_equator = 16 +nnue = 1.421954234999705e+33 +nnua = 1.9146237131657563e+33 +nnux = 1.9645407875568215e+33 +fnue = np.array([0.0974572, 0.04217632, -0.13433261]) +fnua = np.array([0.07237959, 0.03132354, -0.3446878]) +fnux = np.array([-0.02165833, 0.07431613, -0.53545951]) +energy_erg = 20.05473294163565 * 1e6*amrex.eV + +nnu = np.zeros((2,NF)) +nnu[0,0] = nnue +nnu[1,0] = nnua +nnu[:,1:] = nnux / 4. + +fnu = np.zeros((2,NF,3)) +fnu[0,0,:] = nnue * fnue +fnu[1,0,:] = nnua * fnua +fnu[:,1:,:] = nnu[:,1:,np.newaxis] * fnux[np.newaxis,np.newaxis,:] + +particles = moment_interpolate_particles(nphi_equator, nnu, fnu, energy_erg, uniform_sphere, minerbo_interpolate) # [particle, variable] + +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/initial_conditions/st6_emu_pointwise.py b/Scripts/initial_conditions/st6_emu_pointwise.py new file mode 100644 index 00000000..2b9c9178 --- /dev/null +++ b/Scripts/initial_conditions/st6_emu_pointwise.py @@ -0,0 +1,192 @@ +import h5py +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../visualization") +from initial_condition_tools import write_particles +import amrex_plot_tools as amrex + +# generation parameters +emu_filename = "/mnt/scratch/crossing_comparison/sedonu_data/NsNs_LS220-q1-D24_Ev_Lev0_AA-Merger_M1MC/run_8x16/fluid_00001.h5" +iEmu = 126 +jEmu = 103 +kEmu = 12 +NF = 2 +refine_factor = 3 + +# open the emu file +infile = h5py.File(emu_filename,"r") + +# get the volume of the chosen grid cell +xgrid = np.array(infile["axes/x0(cm)[edge]"]) +ygrid = np.array(infile["axes/x1(cm)[edge]"]) +zgrid = np.array(infile["axes/x2(cm)[edge]"]) +dx = xgrid[iEmu+1]-xgrid[iEmu] +dy = ygrid[jEmu+1]-ygrid[jEmu] +dz = zgrid[kEmu+1]-zgrid[kEmu] +dV = dx * dy * dz + +# get the angular grid +costheta_grid = np.array(infile["axes/distribution_costheta_grid(lab)[edge]"]) +costheta_mid = np.array(infile["axes/distribution_costheta_grid(lab)[mid]"]) +phi_grid = np.array(infile["axes/distribution_phi_grid(radians,lab)[edge]"]) +phi_mid = np.array(infile["axes/distribution_phi_grid(radians,lab)[mid]"]) +energy_grid = np.array(infile["axes/frequency(Hz)[edge]"]) * amrex.hbar*2.*np.pi +energy_mid = np.array(infile["axes/frequency(Hz)[mid]" ]) * amrex.hbar*2.*np.pi +ncostheta = len(costheta_mid) +nphi = len(phi_mid) + +# get the full distribution [nu/antinu, energy, mu, phi] +fee = np.array(infile["distribution0(erg|ccm,tet)"][iEmu][jEmu][kEmu]) +feebar = np.array(infile["distribution1(erg|ccm,tet)"][iEmu][jEmu][kEmu]) +f = np.array([fee,feebar]) +infile.close() + +# integrate over energy to get the energy density, number density, and average energy +energy_density = np.sum(f, axis=1) +number_density = np.sum(f / energy_mid[np.newaxis,:,np.newaxis,np.newaxis], axis=1) +energy_erg = np.sum(energy_density) / np.sum(number_density) + + + +# refine the distribution +def refine_grid(grid): + grid_refined = [] + for i in range(len(grid)-1): + delta = (grid[i+1]-grid[i]) / refine_factor + for j in range(refine_factor): + grid_refined.append(grid[i] + delta*j) + grid_refined.append(grid[-1]) + grid_refined = np.array(grid_refined) + mid_refined = np.array([(grid_refined[i] + grid_refined[i+1])/2. for i in range(len(grid_refined)-1)]) + return mid_refined, grid_refined + +phi_mid_refined, phi_grid_refined = refine_grid(phi_grid) +costheta_mid_refined, costheta_grid_refined = refine_grid(costheta_grid) +theta_mid_refined = np.arccos(costheta_mid_refined) +sintheta_mid_refined = np.sin(theta_mid_refined) + +ncostheta_refined = len(costheta_mid_refined) +nphi_refined = len(phi_mid_refined) + +# interpolate the distribution +def interpolate_2d_polar_1point(costheta_mid, phi_mid, f, costheta, phi): + m,n = f.shape + pole0 = np.average(f[ 0,:]) + pole1 = np.average(f[-1,:]) + + if phi <= phi_mid[0] or phi>=phi_mid[-1]: + jL = -1 + jR = 0 + else: + jL = np.where(phi_midphi)[0][ 0] + phiL = phi_mid[jL] + phiR = phi_mid[jR] + + if costheta >= costheta_mid[-1]: + iL = -1 + costhetaL = costheta_mid[iL] + costhetaR = 1 + fLL = f[iL,jL] + fLR = f[iL,jR] + fRL = pole1 + fRR = pole1 + elif costheta <= costheta_mid[0]: + iR = 0 + costhetaL = -1 + costhetaR = costheta_mid[iR] + fLL = pole0 + fLR = pole0 + fRL = f[iR,jL] + fRR = f[iR,jR] + else: + iL = np.where(costheta_midcostheta)[0][ 0] + costhetaL = costheta_mid[iL] + costhetaR = costheta_mid[iR] + fLL = f[iL,jL] + fLR = f[iL,jR] + fRL = f[iR,jL] + fRR = f[iR,jR] + + # calculate the coefficients + dcosthetaL = costheta - costhetaL + dcosthetaR = costhetaR - costheta + dphiL = phi - phiL + dphiR = phiR - phi + dV = (costhetaR-costhetaL) * (phiR-phiL) + cLL = dcosthetaR * dphiR / dV + cLR = dcosthetaR * dphiL / dV + cRL = dcosthetaL * dphiR / dV + cRR = dcosthetaL * dphiL / dV + + # evaluate the result + f_interpolated = (fLL*cLL + fLR*cLR + fRL*cRL + fRR*cRR) / refine_factor**2 + + return f_interpolated + +def interpolate_2d_polar(costheta_mid, phi_mid, f, costheta_mid_refined, phi_mid_refined): + result = np.array([[ interpolate_2d_polar_1point(costheta_mid, phi_mid, f, costheta_mid_refined[i], phi_mid_refined[j]) + for j in range(nphi_refined)] + for i in range(ncostheta_refined)]) + return result + +# interpolate onto the new mesh +number_density_refined = np.array([ + interpolate_2d_polar(costheta_mid, phi_mid, number_density[i], costheta_mid_refined, phi_mid_refined) + for i in range(2)]) +energy_density_refined = np.array([ + interpolate_2d_polar(costheta_mid, phi_mid, energy_density[i], costheta_mid_refined, phi_mid_refined) + for i in range(2)]) +shape = number_density_refined.shape +nparticles = shape[1]*shape[2] + +# print useful quantities +N = np.sum(number_density_refined, axis=(1,2)) +Fx = np.sum(number_density_refined * sintheta_mid_refined[np.newaxis,:,np.newaxis]*np.cos(phi_mid_refined[np.newaxis,np.newaxis,:]), axis=(1,2)) / N +Fy = np.sum(number_density_refined * sintheta_mid_refined[np.newaxis,:,np.newaxis]*np.sin(phi_mid_refined[np.newaxis,np.newaxis,:]), axis=(1,2)) / N +Fz = np.sum(number_density_refined * costheta_mid_refined[np.newaxis,:,np.newaxis], axis=(1,2)) / N +print("Writing",ncostheta_refined*nphi_refined,"particles") +print("ncostheta =",ncostheta_refined) +print("nphi =",nphi_refined) +print("[x,y,z](km) = [",(xgrid[iEmu]+dx/2)/1e5,(ygrid[jEmu]+dy/2)/1e5,(zgrid[kEmu]+dz/2)/1e5,"]") +print("Nee =",N[0],"cm^-3") +print("Neebar =",N[1],"cm^-3") +print("fluxfac_ee = [",Fx[0],Fy[0],Fz[0],"]") +print("fluxfac_eebar = [",Fx[1],Fy[1],Fz[1],"]") +print("|fluxfac_ee| =",np.sqrt(Fx[0]**2+Fy[0]**2+Fz[0]**2)) +print("|fluxfac_eebar| =",np.sqrt(Fx[1]**2+Fy[1]**2+Fz[1]**2)) + +# get variable keys +rkey, ikey = amrex.get_particle_keys(NF,ignore_pos=True) +nelements = len(rkey) + +# generate list of particles +particles = np.zeros((nparticles, nelements)) +for i in range(ncostheta_refined): + ctheta = costheta_mid_refined[i] + theta = np.arccos(ctheta) + stheta = np.sin(theta) + for j in range(nphi_refined): + cphi = np.cos(phi_mid_refined[i]) + sphi = np.sin(phi_mid_refined[i]) + + index = i*ncostheta_refined + j + p = particles[index] + + u = np.array([stheta*cphi, stheta*sphi, ctheta]) + p[rkey["pupt"]] = energy_erg + p[rkey["pupx"]] = u[0] * energy_erg + p[rkey["pupy"]] = u[1] * energy_erg + p[rkey["pupz"]] = u[2] * energy_erg + + p[rkey["N" ]] = number_density_refined[0,i,j] + p[rkey["Nbar"]] = number_density_refined[1,i,j] + p[rkey["f00_Re" ]] = 1 + p[rkey["f00_Rebar"]] = 1 + + +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/initial_conditions/st7_empty_particles.py b/Scripts/initial_conditions/st7_empty_particles.py new file mode 100644 index 00000000..5fa0b749 --- /dev/null +++ b/Scripts/initial_conditions/st7_empty_particles.py @@ -0,0 +1,72 @@ +''' +Created by Erick Urquilla, Department of Physics and Astronomy, University of Tennessee, Knoxville. +This script is used to create the empty monoenergetic particles +''' +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_reduction") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +NF = 3 # Number of flavors +nphi_equator = 16 # number of direction in equator ---> theta = pi/2 + +nu_e = 0.0 # 1/ccm +nu_x = 0.0 # 1/ccm +nu_ebar = 0.0 # 1/ccm +nu_xbar = 0.0 # 1/ccm + +# Energy bin size +energy_bin_size_MeV = 0.8339001570751987 # Energy in Mev + +# Energy bin centers extracted from NuLib table +energies_center_Mev = [50.0] # Energy in Mev +# Energy bin bottom extracted from NuLib table +energies_bottom_Mev = [50.0-energy_bin_size_MeV/2.0] +# Energy bin top extracted from NuLib table +energies_top_Mev = [50.0+energy_bin_size_MeV/2.0] + +# Energies in ergs +energies_center_erg = np.array(energies_center_Mev) * 1e6*amrex.eV # Energy in ergs +energies_bottom_erg = np.array(energies_bottom_Mev) * 1e6*amrex.eV # Energy in ergs +energies_top_erg = np.array(energies_top_Mev ) * 1e6*amrex.eV # Energy in ergs + +# Generate the number of energy bins +n_energies = len(energies_center_erg) + +# Get variable keys +rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + +# Generate the number of variables that describe each particle +n_variables = len(rkey) + +# Get the momentum distribution of the particles +phat = uniform_sphere(nphi_equator) + +# Generate the number of directions +n_directions = len(phat) + +# Generate the number of particles +n_particles = n_energies * n_directions + +# Initialize a NumPy array to store all particles +particles = np.zeros((n_energies, n_directions, n_variables)) + +# Fill the particles array using a loop, replacing append +for i, energy_bin in enumerate(energies_center_erg): + particles[i , : , rkey["pupx"] : rkey["pupz"]+1 ] = energy_bin * phat + particles[i , : , rkey["pupt"] ] = energy_bin + particles[i , : , rkey["Vphase"] ] = ( 4.0 * np.pi / n_directions ) * ( ( energies_top_erg[i] ** 3 - energies_bottom_erg[i] ** 3 ) / 3.0 ) + particles[i , : , rkey["N00_Re"] ] = nu_e + particles[i , : , rkey["N11_Re"] ] = nu_x + particles[i , : , rkey["N00_Rebar"] ] = nu_ebar + particles[i , : , rkey["N11_Rebar"] ] = nu_xbar + +# Reshape the particles array +particles = particles.reshape(n_energies * n_directions, n_variables) + +# Write particles initial condition file +write_particles(np.array(particles), NF, "particle_input.dat") \ No newline at end of file diff --git a/Scripts/initial_conditions/st8_coll_inst_test.py b/Scripts/initial_conditions/st8_coll_inst_test.py new file mode 100644 index 00000000..665b44c1 --- /dev/null +++ b/Scripts/initial_conditions/st8_coll_inst_test.py @@ -0,0 +1,72 @@ +''' +Created by Erick Urquilla, Department of Physics and Astronomy, University of Tennessee, Knoxville. +This script is used to create the particle initial conditions that attempt to replicate the +simulation in the paper Collisional Flavor Instabilities of Supernova Neutrinos by L. Johns +[2104.11369]. +''' +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_reduction") +from initial_condition_tools import uniform_sphere, moment_interpolate_particles, minerbo_interpolate, write_particles +import amrex_plot_tools as amrex + +# These initial conditions are intended to replicate the collisional instability outputs in +# "Collisional Flavor Instabilities of Supernova Neutrinos" by L. Johns [2104.11369]. + +NF = 2 # Number of flavors +nphi_equator = 16 # number of direction in equator ---> theta = pi/2 + +# Neutrino number densities +nnue = 3.0e+33 # 1/ccm +nnua = 2.5e+33 # 1/ccm +nnux = 1.0e+33 # 1/ccm + +# Neutrino flux factors +fnue = np.array([0.0 , 0.0 , 0.0]) +fnua = np.array([0.0 , 0.0 , 0.0]) +fnux = np.array([0.0 , 0.0 , 0.0]) + +# Energy bin size +energy_bin_size_MeV = 2.272540842052914 # Energy in Mev + +# Energy bin centers extracted from NuLib table +energies_center_Mev = 20.0 # Energy in Mev +# Energy bin bottom extracted from NuLib table +energies_bottom_Mev = 20.0-energy_bin_size_MeV/2.0 +# Energy bin top extracted from NuLib table +energies_top_Mev = 20.0+energy_bin_size_MeV/2.0 + +# Energies in ergs +energies_center_erg = np.array(energies_center_Mev) * 1e6*amrex.eV # Energy in ergs +energies_bottom_erg = np.array(energies_bottom_Mev) * 1e6*amrex.eV # Energy in ergs +energies_top_erg = np.array(energies_top_Mev ) * 1e6*amrex.eV # Energy in ergs + +# Matrix to save the neutrino number densities +nnu = np.zeros((2,NF)) +nnu[0,0] = nnue +nnu[1,0] = nnua +nnu[:,1:] = nnux + +# Matrix to save the neutrino number densities fluxes +fnu = np.zeros((2,NF,3)) +fnu[0,0,:] = nnue * fnue +fnu[1,0,:] = nnua * fnua +fnu[:,1:,:] = nnu[:,1:,np.newaxis] * fnux[np.newaxis,np.newaxis,:] + +# Generate particles +particles = moment_interpolate_particles(nphi_equator, nnu, fnu, energies_center_erg, uniform_sphere, minerbo_interpolate) # [particle, variable] + +# Generate the number of directions +n_directions = len(particles) + +# Compute the phase space volume dOmega * dE^3 / 3 +Vphase = ( 4.0 * np.pi / n_directions ) * ( ( energies_top_erg ** 3 - energies_bottom_erg ** 3 ) / 3.0 ) + +# Save V_phase in the last column of the particle array +particles[:,-1] = Vphase + +# Write particles initial condition file +write_particles(np.array(particles), NF, "particle_input.dat") \ No newline at end of file diff --git a/Scripts/initial_conditions/st9_empty_particles_multi_energy.py b/Scripts/initial_conditions/st9_empty_particles_multi_energy.py new file mode 100644 index 00000000..4ec43d26 --- /dev/null +++ b/Scripts/initial_conditions/st9_empty_particles_multi_energy.py @@ -0,0 +1,62 @@ +''' +Created by Erick Urquilla, Department of Physics and Astronomy, University of Tennessee, Knoxville. +This script is used to create empty particles at the energy bin center of the Nulib table. +''' +import numpy as np +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_analysis") +from initial_condition_tools import uniform_sphere, write_particles +import amrex_plot_tools as amrex + +# generation parameters +# MUST MATCH THE INPUTS IN THE EMU INPUT FILE! +nphi_equator = 16 # number of direction in equator +NF = 3 # number of flavors + +# Energy bin centers extracted from NuLib table +energies_center_Mev = [1, 3, 5.23824, 8.00974, 11.4415, 15.6909, 20.9527, 27.4681, 35.5357, 45.5254, 57.8951, 73.2117, 92.1775, 115.662, 144.741, 180.748, 225.334, 280.542] # Energy in Mev +# Energy bin bottom extracted from NuLib table +energies_bottom_Mev = [0, 2, 4, 6.47649, 9.54299, 13.3401, 18.0418, 23.8636, 31.0725, 39.9989, 51.0519, 64.7382, 81.6853, 102.67, 128.654, 160.828, 200.668, 250] +# Energy bin top extracted from NuLib table +energies_top_Mev = [2, 4, 6.47649, 9.54299, 13.3401, 18.0418, 23.8636, 31.0725, 39.9989, 51.0519, 64.7382, 81.6853, 102.67, 128.654, 160.828, 200.668, 250, 311.085] + +# Energies in ergs +energies_center_erg = np.array(energies_center_Mev) * 1e6*amrex.eV # Energy in ergs +energies_bottom_erg = np.array(energies_bottom_Mev) * 1e6*amrex.eV # Energy in ergs +energies_top_erg = np.array(energies_top_Mev ) * 1e6*amrex.eV # Energy in ergs + +# Generate the number of energy bins +n_energies = len(energies_center_erg) + +# Get variable keys +rkey, ikey = amrex.get_particle_keys(NF, ignore_pos=True) + +# Generate the number of variables that describe each particle +n_variables = len(rkey) + +# Get the momentum distribution of the particles +phat = uniform_sphere(nphi_equator) + +# Generate the number of directions +n_directions = len(phat) + +# Generate the number of particles +n_particles = n_energies * n_directions + +# Initialize a NumPy array to store all particles +particles = np.zeros((n_energies, n_directions, n_variables)) + +# Fill the particles array using a loop, replacing append +for i, energy_bin in enumerate(energies_center_erg): + particles[i , : , rkey["pupx"] : rkey["pupz"]+1 ] = energy_bin * phat + particles[i , : , rkey["pupt"] ] = energy_bin + particles[i , : , rkey["Vphase"] ] = ( 4.0 * np.pi / n_directions ) * ( ( energies_top_erg[i] ** 3 - energies_bottom_erg[i] ** 3 ) / 3.0 ) + +# Reshape the particles array +particles = particles.reshape(n_energies * n_directions, n_variables) + +# Write particles initial condition file +write_particles(np.array(particles), NF, "particle_input.dat") diff --git a/Scripts/runtools/.gitignore b/Scripts/runtools/.gitignore new file mode 100644 index 00000000..c24e9012 --- /dev/null +++ b/Scripts/runtools/.gitignore @@ -0,0 +1,2 @@ +*~ +__pycache__ \ No newline at end of file diff --git a/Scripts/runtools/runtools.py b/Scripts/runtools/runtools.py new file mode 100644 index 00000000..c1ba6ab5 --- /dev/null +++ b/Scripts/runtools/runtools.py @@ -0,0 +1,159 @@ +import os +import shutil +import glob + +n_digits_rundir = 3 +rundir_base = "RUN" + +# write an input file in a particular directory with a set of inputs +def write_inputfile(directory, inputs_dict, filename="inputs"): + path = directory+"/"+filename + f = open(path,"w") + for key in inputs_dict: + f.write(key+" = ") + value = inputs_dict[key] + wrapper = "\"" if type(value)==str else "" + f.write(wrapper) + if type(value)==list: + first = True + for elem in value: + if first: f.write("(") + else: f.write(",") + first = False + f.write(str(elem)) + f.write(")") + else: + f.write(str(inputs_dict[key])) + f.write(wrapper) + f.write("\n") + f.close() + +def clean_string(s): + for character in ["\"","\'"," ","\n"]: + s = s.replace(character,"") + return s + +# given a run directory and an optional inputs filename, +# return a dictionary with key-value pairs for the parameters +# keys and values are both returned as strings +def read_inputfile(directory, filename="inputs"): + path = directory + "/"+filename + f = open(path,"r") + inputs_dict = {} + for line in f.readlines(): + # remove comments + line = line.split("#")[0] + + # skip empty lines + if "=" not in line: + continue + + # split line into key/value + key,value = line.split("=") + + # get the format + is_string = any((c in value) for c in ['\"','\'']) + is_list = "(" in value + is_float = (not is_string) and (("." in value) or ("e" in value) ) + is_int = (not is_float) and (not is_string) + + if(is_int): cast_func = int + if(is_float): cast_func = float + if(is_string): cast_func = clean_string + + if is_list: + value = value.replace("(","") + value = value.replace(")","") + value = value.split(",") + value = [cast_func(v) for v in value] + else: + value = cast_func(value) + + key = key.strip() + + # append to the dictionary + inputs_dict[key] = value + + f.close() + return inputs_dict + +# set up a new simulation in the specified location with an inputs dictionary and executable +# Refuse to overwrite unless overwrite==True +# note that the simulation still lacks a particle file! Must be created separately. +def create_new_simulation(directory, inputs_dict, executable_path, overwrite=False): + + # check whether the directory already exiss + if os.path.exists(directory): + if not overwrite: + raise(directory+" already exists. Cannot create ") + else: + os.system('rm -rf '+directory) + print(" Creating",directory) + + # create the new directories + os.mkdir(directory) + subdir = directory+"/"+rundir_base+str(1).zfill(n_digits_rundir) + os.mkdir(subdir) + + # copy in the executable + os.system("cp "+executable_path+" "+subdir) + + # write the inputs file + write_inputfile(subdir, inputs_dict) + + return subdir + +def out_step_list(directory,subdir=None): + search_string = directory+"/plt?????" + if subdir==None: + dirlist = glob.glob(search_string) + steplist = sorted([int(d.split("/")[-1][3:]) for d in dirlist]) + else: + search_string = search_string+"/"+subdir + dirlist = glob.glob(search_string) + steplist = sorted([int(d.split("/")[-2][3:]) for d in dirlist]) + return steplist + +# create a new run subdirectory based on the previous one +# replace the parameters and executable as necessary +def create_restart_simulation(directory, replace_inputs_dict=None, executable_path=None): + # determine new run dir + rundirlist = sorted(glob.glob(directory+"/"+rundir_base+"*")) + assert(len(rundirlist)>0) + lastrun = int(rundirlist[-1].split("/")[-1].replace(rundir_base,"").strip("0")) + nextrundir = directory+"/"+rundir_base+str(lastrun+1).zfill(n_digits_rundir) + os.mkdir(nextrundir) + + # print some diagnostics + print(" Restarting",directory,"at",nextrundir) + last_step_list = out_step_list(rundirlist[-1]) + print(" "+rundirlist[-1]+" outputs grid data from",str(last_step_list[0]),"to",str(last_step_list[-1])) + particle_step_list = out_step_list(rundirlist[-1],"neutrinos") + print(" "+rundirlist[-1]+" outputs particle data from",str(particle_step_list[0]),"to",str(particle_step_list[-1])) + assert(len(particle_step_list)>0) + + # get inputs dict from previous simulation + inputs_dict = read_inputfile(rundirlist[-1]) + + # activate restarting + inputs_dict["do_restart"] = 1 + + # set restart directory + inputs_dict["restart_dir"] = "../"+rundirlist[-1].split("/")[-1]+"/plt"+str(particle_step_list[-1]).zfill(5) + + # use replace_inputs_dict last to override anything else + if replace_inputs_dict != None: + print("replacing dict") + for key in replace_inputs_dict: + print(key in inputs_dict) + inputs_dict[key] = replace_inputs_dict[key] + + # write the inputs file + write_inputfile(nextrundir,inputs_dict) + + # copy in executable + if executable_path==None: + executable_path = rundirlist[-1]+"/*.ex" + os.system("cp "+executable_path+" "+nextrundir) + + return nextrundir diff --git a/Scripts/symbolic_hermitians/generate_code.py b/Scripts/symbolic_hermitians/generate_code.py index 397f40f8..2e223e95 100755 --- a/Scripts/symbolic_hermitians/generate_code.py +++ b/Scripts/symbolic_hermitians/generate_code.py @@ -4,10 +4,8 @@ import argparse import os import sympy -from sympy.codegen.ast import Assignment -from HermitianUtils import HermitianMatrix,SU_vector_ideal_magnitude +from HermitianUtils import HermitianMatrix import shutil -import math parser = argparse.ArgumentParser(description="Generates code for calculating C = i * [A,B] for symbolic NxN Hermitian matrices A, B, C, using real-valued Real and Imaginary components.") parser.add_argument("N", type=int, help="Size of NxN Hermitian matrices.") @@ -15,9 +13,15 @@ parser.add_argument("-eh", "--emu_home", type=str, default=".", help="Path to Emu home directory.") parser.add_argument("-c", "--clean", action="store_true", help="Clean up any previously generated files.") parser.add_argument("-rn", "--rhs_normalize", action="store_true", help="Normalize F when applying the RHS update F += dt * dFdt (limits to 2nd order in time).") +parser.add_argument("-nm", "--num_moments", type=int, default=2, help="Number of moments to compute.") args = parser.parse_args() +# make sure arguments make sense +assert(args.N>0) +assert(args.num_moments>=2) # just N and F +assert(args.num_moments<=3) # also include P. Higher moments not implemented + def write_code(code, output_file, template=None): ## If a template file is supplied, this will insert the generated code ## where the "<>code<>" string is found. @@ -90,31 +94,22 @@ def delete_generated_files(): #==================================# # FlavoredNeutrinoContainer.H_fill # #==================================# - vars = ["f"] + vars = ["N"] tails = ["","bar"] code = [] for t in tails: - code += ["N"+t] # number of neutrinos - code += ["L"+t] # length of isospin vector, units of number of neutrinos for v in vars: A = HermitianMatrix(args.N, v+"{}{}_{}"+t) code += A.header() + code += ["TrHN"] + code += ["Vphase"] - code = [code[i]+"," for i in range(len(code))] - write_code(code, os.path.join(args.emu_home, "Source/generated_files", "FlavoredNeutrinoContainer.H_fill")) + code_lines = [code[i]+"," for i in range(len(code))] + write_code(code_lines, os.path.join(args.emu_home, "Source/generated_files", "FlavoredNeutrinoContainer.H_fill")) #========================================================# # FlavoredNeutrinoContainerInit.H_particle_varnames_fill # #========================================================# - vars = ["f"] - tails = ["","bar"] - code = [] - for t in tails: - code += ["N"+t] - code += ["L"+t] - for v in vars: - A = HermitianMatrix(args.N, v+"{}{}_{}"+t) - code += A.header() code_string = 'attribute_names = {"time", "x", "y", "z", "pupx", "pupy", "pupz", "pupt", ' code = ['"{}"'.format(c) for c in code] code_string = code_string + ", ".join(code) + "};" @@ -125,6 +120,8 @@ def delete_generated_files(): # Evolve.H_fill # #===============# vars = ["N","Fx","Fy","Fz"] + if args.num_moments>=3: + vars.extend(["Pxx","Pxy","Pxz","Pyy","Pyz","Pzz"]) tails = ["","bar"] code = [] for v in vars: @@ -138,6 +135,8 @@ def delete_generated_files(): # Evolve.cpp_grid_names_fill # #============================# vars = ["N","Fx","Fy","Fz"] + if args.num_moments>=3: + vars.extend(["Pxx","Pxy","Pxz","Pyy","Pyz","Pzz"]) tails = ["","bar"] code = [] for v in vars: @@ -152,22 +151,96 @@ def delete_generated_files(): #=================================# tails = ["","bar"] string1 = "amrex::Gpu::Atomic::AddNoRet(&sarr(i, j, k, GIdx::" - string2 = "-start_comp), sx(i) * sy(j) * sz(k) * p.rdata(PIdx::" + string2 = "-start_comp), sx(i) * sy(j) * sz(k) * inv_cell_volume * p.rdata(PIdx::" string4 = [");", "*p.rdata(PIdx::pupx)/p.rdata(PIdx::pupt));", "*p.rdata(PIdx::pupy)/p.rdata(PIdx::pupt));", "*p.rdata(PIdx::pupz)/p.rdata(PIdx::pupt));"] deposit_vars = ["N","Fx","Fy","Fz"] + if args.num_moments >= 3: + deposit_vars.extend(["Pxx","Pxy","Pxz","Pyy","Pyz","Pzz"]) + string4.extend(["*p.rdata(PIdx::pupx)*p.rdata(PIdx::pupx)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));", + "*p.rdata(PIdx::pupx)*p.rdata(PIdx::pupy)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));", + "*p.rdata(PIdx::pupx)*p.rdata(PIdx::pupz)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));", + "*p.rdata(PIdx::pupy)*p.rdata(PIdx::pupy)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));", + "*p.rdata(PIdx::pupy)*p.rdata(PIdx::pupz)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));", + "*p.rdata(PIdx::pupz)*p.rdata(PIdx::pupz)/p.rdata(PIdx::pupt)/p.rdata(PIdx::pupt));"]) code = [] for t in tails: - string3 = ")*p.rdata(PIdx::N"+t+")" - flist = HermitianMatrix(args.N, "f{}{}_{}"+t).header() + string3 = ")" + flist = HermitianMatrix(args.N, "N{}{}_{}"+t).header() for ivar in range(len(deposit_vars)): deplist = HermitianMatrix(args.N, deposit_vars[ivar]+"{}{}_{}"+t).header() for icomp in range(len(flist)): code.append(string1+deplist[icomp]+string2+flist[icomp]+string3+string4[ivar]) write_code(code, os.path.join(args.emu_home, "Source/generated_files", "Evolve.cpp_deposit_to_mesh_fill")) + #================================# + # DataReducer.cpp_fill_particles # + #================================# + tails = ["","bar"] + code = [] + for t in tails: + # diagonal averages + N = HermitianMatrix(args.N, "p.rdata(PIdx::N{}{}_{}"+t+")") + Nlist = N.header_diagonals(); + for i in range(len(Nlist)): + code.append("TrN += "+Nlist[i]+";") + + write_code(code, os.path.join(args.emu_home, "Source/generated_files", "DataReducer.cpp_fill_particles")) + + #======================# + # DataReducer.cpp_fill # + #======================# + tails = ["","bar"] + code = [] + for t in tails: + # diagonal averages + N = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::N{}{}_{}"+t+")") + Nlist = N.header_diagonals(); + Fx = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Fx{}{}_{}"+t+")") + Fxlist = Fx.header_diagonals(); + Fy = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Fy{}{}_{}"+t+")") + Fylist = Fy.header_diagonals(); + Fz = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Fz{}{}_{}"+t+")") + Fzlist = Fz.header_diagonals(); + for i in range(len(Nlist)): + code.append("Ndiag"+t+"["+str(i)+"] = "+Nlist[i]+";") + code.append("Fxdiag"+t+"["+str(i)+"] = "+Fxlist[i]+";") + code.append("Fydiag"+t+"["+str(i)+"] = "+Fylist[i]+";") + code.append("Fzdiag"+t+"["+str(i)+"] = "+Fzlist[i]+";") + + if args.num_moments>=3: + Pxx = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pxx{}{}_{}"+t+")") + Pxxlist = Pxx.header_diagonals(); + Pxy = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pxy{}{}_{}"+t+")") + Pxylist = Pxy.header_diagonals(); + Pxz = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pxz{}{}_{}"+t+")") + Pxzlist = Pxz.header_diagonals(); + Pyy = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pyy{}{}_{}"+t+")") + Pyylist = Pyy.header_diagonals(); + Pyz = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pyz{}{}_{}"+t+")") + Pyzlist = Pyz.header_diagonals(); + Pzz = HermitianMatrix(args.N, "a(i\,j\,k\,GIdx::Pzz{}{}_{}"+t+")") + Pzzlist = Pzz.header_diagonals(); + for i in range(len(Nlist)): + code.append("Pxxdiag"+t+"["+str(i)+"] = "+Pxxlist[i]+";") + code.append("Pxydiag"+t+"["+str(i)+"] = "+Pxylist[i]+";") + code.append("Pxzdiag"+t+"["+str(i)+"] = "+Pxzlist[i]+";") + code.append("Pyydiag"+t+"["+str(i)+"] = "+Pyylist[i]+";") + code.append("Pyzdiag"+t+"["+str(i)+"] = "+Pyzlist[i]+";") + code.append("Pzzdiag"+t+"["+str(i)+"] = "+Pzzlist[i]+";") + + # off-diagonal magnitude + mag2 = 0 + for i in range(N.size): + for j in range(i+1,N.size): + re,im = N.H[i,j].as_real_imag() + mag2 += re**2 + im**2 + code.append("N_offdiag_mag2 += "+sympy.cxxcode(sympy.simplify(mag2))+";") + + write_code(code, os.path.join(args.emu_home, "Source/generated_files", "DataReducer.cpp_fill")) + #==================# # Evolve.H_M2_fill # #==================# @@ -259,7 +332,6 @@ def delete_generated_files(): #============================# code = [] length = sympy.symbols("length",real=True) - cell_volume = sympy.symbols("cell_volume",real=True) rho = sympy.symbols("fab(i\,j\,k\,GIdx\:\:rho)",real=True) Ye = sympy.symbols("fab(i\,j\,k\,GIdx\:\:Ye)",real=True) mp = sympy.symbols("PhysConst\:\:Mp",real=True) @@ -269,7 +341,7 @@ def delete_generated_files(): N = HermitianMatrix(args.N, "fab(i\,j\,k\,GIdx::N{}{}_{})") Nbar = HermitianMatrix(args.N, "fab(i\,j\,k\,GIdx::N{}{}_{}bar)") HSI = (N-Nbar.conjugate()) - HSI.H[0,0] += rho*Ye/mp * cell_volume + HSI.H[0,0] += rho*Ye/mp V_adaptive2 = HSI.SU_vector_magnitude2() code.append("V_adaptive2 += "+sympy.cxxcode(sympy.simplify(V_adaptive2))+";") @@ -282,15 +354,15 @@ def delete_generated_files(): code.append("V_adaptive2 += "+sympy.cxxcode(sympy.simplify(V_adaptive2))+";") # put in the units - code.append("V_adaptive = sqrt(V_adaptive2)*"+sympy.cxxcode(sqrt2GF/cell_volume)+";") + code.append("V_adaptive = sqrt(V_adaptive2)*"+sympy.cxxcode(sqrt2GF)+";") # old "stupid" way of computing the timestep. # the factor of 2 accounts for potential worst-case effects of neutrinos and antineutrinos for i in range(args.N): code.append("V_stupid = max(V_stupid,"+sympy.cxxcode(N.H[i,i])+");") code.append("V_stupid = max(V_stupid,"+sympy.cxxcode(Nbar.H[i,i])+");") - code.append("V_stupid = max(V_stupid,"+sympy.cxxcode(rho*Ye/mp*cell_volume)+");") - code.append("V_stupid *= "+sympy.cxxcode(2.0*args.N*sqrt2GF/cell_volume)+";") + code.append("V_stupid = max(V_stupid,"+sympy.cxxcode(rho*Ye/mp)+");") + code.append("V_stupid *= "+sympy.cxxcode(2.0*args.N*sqrt2GF)+";") write_code(code, os.path.join(args.emu_home,"Source/generated_files","Evolve.cpp_compute_dt_fill")) #=======================================# @@ -309,7 +381,7 @@ def delete_generated_files(): Vlist = HermitianMatrix(args.N, "V{}{}_{}").header() Nlist = HermitianMatrix(args.N, "N{}{}_{}").header() Flist = [HermitianMatrix(args.N, "F"+d+"{}{}_{}").header() for d in direction] - rhoye = string_interp+"rho)*"+string_interp+"Ye)/PhysConst::Mp/inv_cell_volume" + rhoye = string_interp+"rho)*"+string_interp+"Ye)/PhysConst::Mp" code.append("double SI_partial, SI_partialbar, inside_parentheses;") code.append("") @@ -351,107 +423,107 @@ def sgn(t,var): else: line += " -= " - line += "sqrt(2.) * PhysConst::GF * inv_cell_volume * sx(i) * sy(j) * sz(k) * (inside_parentheses);" + line += "sqrt(2.) * PhysConst::GF * sx(i) * sy(j) * sz(k) * (inside_parentheses);" code.append(line) code.append("") + + code.append("T_pp += sx(i) * sy(j) * sz(k) * sarr(i, j, k, GIdx::T);") + code.append("Ye_pp += sx(i) * sy(j) * sz(k) * sarr(i, j, k, GIdx::Ye);") + code.append("rho_pp += sx(i) * sy(j) * sz(k) * sarr(i, j, k, GIdx::rho);") + code.append("") + write_code(code, os.path.join(args.emu_home, "Source/generated_files", "Evolve.cpp_interpolate_from_mesh_fill")) #========================# # Evolve.cpp_dfdt_fill # #========================# - # Set up Hermitian matrices A, B, C + # Generate quantum kinetic equations + + # Define useful constants hbar = sympy.symbols("PhysConst\:\:hbar",real=True) + attenuation_to_hamiltonian = sympy.symbols("parms->attenuation_hamiltonians", real=True) + V_phase = sympy.symbols("p.rdata(PIdx\:\:Vphase)", real=True) + pi = sympy.symbols("MathConst\:\:pi", real=True) + c = sympy.symbols("PhysConst\:\:c", real=True) + + # List that will store the QKE code. code = [] + + # Looping over neutrinos(tail: no tail) and antineutrinos(tail: bar) for t in tails: - H = HermitianMatrix(args.N, "V{}{}_{}"+t) - F = HermitianMatrix(args.N, "p.rdata(PIdx::f{}{}_{}"+t+")") - # G = Temporary variables for dFdt - G = HermitianMatrix(args.N, "dfdt{}{}_{}"+t) + # Define Fermi-Dirac distribution matrix f_eq = diag( f_e , f_x ) from input parameters + f_eq = HermitianMatrix(args.N, "f_eq_{}{}_{}"+t) # Fermi-dirac distribution matrix ----> To be used in calculation of QKE in sympy format + f_eq_cpp = HermitianMatrix(args.N, "f_eq"+t+"[{}][{}]") # Fermi-dirac distribution matrix ----> Using the systaxis of line 183 of the Evolve.cpp file + f_eq.H = f_eq_cpp.H # Assigning input mean free paths to SymPy matrix + f_eq_temp_declare = ["amrex::Real {}".format(line) for line in f_eq.code()] # + code.append(f_eq_temp_declare) + + # Define Gamma matrix from input parameters : Gamma = diag( k*_e , k*_x ) / 2 . ka is the inverse mean free path for flavor a, including Pauli blocking term. * means that Pauli blocking term is already in the inverse mean free path values. + Gamma = HermitianMatrix(args.N, "Gamma_{}{}_{}"+t) # Inverse mean free path matrix. Gamma = diag( k*e , k*x ) / 2. ----> To be used in calculation of QKE in sympy format + IMFP_abs = HermitianMatrix(args.N, "IMFP_abs"+t+"[{}][{}]") # Inverse mean free path matrix IMFP_abs = diag( k*e , k*x ) ----> Using the systaxis of line 181 of the Evolve.cpp file + Gamma.H = IMFP_abs.H / 2 # Compute Gamma + Gamma_temp_declare = ["amrex::Real {}".format(line) for line in Gamma.code()] + code.append(Gamma_temp_declare) + + # Define N_eq matrix + f_eq = HermitianMatrix(args.N, "f_eq_{}{}_{}"+t) # Fermi-dirac distribution matrix f_eq = diag( fe , fx ) + N_eq = HermitianMatrix(args.N, "N_eq_{}{}_{}"+t) # Equilibrium neutrino number matrix N_eq equals the integral of f_eq, where the integral is over the phase space that the particle represents. + N_eq.H = f_eq.H * V_phase / ( 2 * pi * hbar * c )**3 + N_eq_temp_declare = ["amrex::Real {}".format(line) for line in N_eq.code()] + code.append(N_eq_temp_declare) + + # Define collision term + Gamma = HermitianMatrix(args.N, "Gamma_{}{}_{}"+t) # Inverse mean free path matrix. Gamma = diag( k*e , k*x ) / 2. ka is the inverse mean free path for flavor a, including Pauli blocking term. + N = HermitianMatrix(args.N, "p.rdata(PIdx::N{}{}_{}"+t+")") # Neutrino number matrix + N_eq = HermitianMatrix(args.N, "N_eq_{}{}_{}"+t) # Equilibrium neutrino number matrix N_eq equals the integral of f_eq, where the integral is over the phase space that the particle represents. + C = HermitianMatrix(args.N, "C_{}{}_{}"+t) # Collision term C = { gamma , N_eq - N }, {} means anticonmutator + C.H = Gamma.H * ( N_eq.H - N.H ) + ( N_eq.H - N.H ) * Gamma.H # Compute collision term + C_temp_declare = ["amrex::Real {}".format(line) for line in C.code()] + code.append(C_temp_declare) + + # Writing QKE + C = HermitianMatrix(args.N, "C_{}{}_{}"+t) # Collision term C = { gamma , N_eq - N }, {} means anticonmutator + H = HermitianMatrix(args.N, "V{}{}_{}"+t) # Hamiltonian + N = HermitianMatrix(args.N, "p.rdata(PIdx::N{}{}_{}"+t+")") # Neutrino number matrix + dNdt_temp = HermitianMatrix(args.N, "dNdt{}{}_{}"+t) # Temporary matrix for dNdt + dNdt_temp.H = C.H * c + ((H*N - N*H).times(-sympy.I/hbar)).H * attenuation_to_hamiltonian # Compute quantum kinetic equation + dNdt_temp_declare = ["amrex::Real {}".format(line) for line in dNdt_temp.code()] + code.append(dNdt_temp_declare) - # Calculate C = i * [A,B] - #Fnew.anticommutator(H,F).times(sympy.I * dt); - G.H = ((H*F - F*H).times(-sympy.I/hbar)).H + # Store dFdt back into the particle data for F + dNdt = HermitianMatrix(args.N, "p.rdata(PIdx::N{}{}_{}"+t+")") + dNdt_empty = HermitianMatrix(args.N, "dNdt{}{}_{}"+t) + dNdt.H = dNdt_empty.H - # Write the temporary variables for dFdt - Gdeclare = ["amrex::Real {}".format(line) for line in G.code()] - code.append(Gdeclare) + # Write out dNdt->N + code.append(dNdt.code()) - # Store dFdt back into the particle data for F - dFdt = HermitianMatrix(args.N, "p.rdata(PIdx::f{}{}_{}"+t+")") - Gempty = HermitianMatrix(args.N, "dfdt{}{}_{}"+t) - dFdt.H = Gempty.H + # store Tr(H*F) for estimating numerical errors + TrHN = (H*N).trace(); + code.append(["p.rdata(PIdx::TrHN) += ("+sympy.cxxcode(sympy.simplify(TrHN))+");"]) - # Write out dFdt->F - code.append(dFdt.code()) code = [line for sublist in code for line in sublist] write_code(code, os.path.join(args.emu_home, "Source/generated_files", "Evolve.cpp_dfdt_fill")) - #================================================# - # FlavoredNeutrinoContainer.cpp_Renormalize_fill # - #================================================# + #========================# + # Evolve.cpp_dfdt_fill_zeros # + #========================# + + # List that will store the code for setting the derivative of the matrices N and Nbar to zero. code = [] - for t in tails: - # make sure the trace is 1 - code.append("sumP = 0;") - f = HermitianMatrix(args.N, "p.rdata(PIdx::f{}{}_{}"+t+")") - fdlist = f.header_diagonals() - flist = f.header() - for fii in fdlist: - code.append("sumP += " + fii + ";") - code.append("error = sumP-1.0;") - code.append('if( std::abs(error) > 100.*parms->maxError) {') - code.append("std::ostringstream Convert;") - code.append('Convert << "Matrix trace (SumP) is not equal to 1, trace error exceeds 100*maxError: " << std::abs(error) << " > " << 100.*parms->maxError;') - code.append("std::string Trace_Error = Convert.str();") - code.append('amrex::Error(Trace_Error);') - code.append("}") - code.append("if( std::abs(error) > parms->maxError ) {") - for fii in fdlist: - code.append(fii + " -= error/"+str(args.N)+";") - code.append("}") - code.append("") - # make sure diagonals are positive - for fii in fdlist: - code.append('if('+fii+'<-100.*parms->maxError) {') - code.append("std::ostringstream Convert;") - code.append('Convert << "Diagonal element '+fii[14:20]+' is negative, less than -100*maxError: " << '+fii+' << " < " << -100.*parms->maxError;') - code.append("std::string Sign_Error = Convert.str();") - code.append('amrex::Error(Sign_Error);') - code.append("}") - code.append("if("+fii+"<-parms->maxError) "+fii+"=0;") - code.append("") + # Looping over neutrinos(tail: no tail) and antineutrinos(tail: bar) + for t in tails: - # make sure the flavor vector length is what it would be with a 1 in only one diagonal - length = sympy.symbols("length",real=True) - length = f.SU_vector_magnitude() - target_length = "p.rdata(PIdx::L"+t+")" - code.append("length = "+sympy.cxxcode(sympy.simplify(length))+";") - code.append("error = length-"+str(target_length)+";") - code.append('if( std::abs(error) > 100.*parms->maxError) {') - code.append("std::ostringstream Convert;") - code.append('Convert << "flavor vector length differs from target length by more than 100*maxError: " << std::abs(error) << " > " << 100.*parms->maxError;') - code.append("std::string Length_Error = Convert.str();") - code.append('amrex::Error(Length_Error);') - code.append("}") - code.append("if( std::abs(error) > parms->maxError) {") - for fii in flist: - code.append(fii+" /= length/"+str(target_length)+";") - code.append("}") - code.append("") - - write_code(code, os.path.join(args.emu_home, "Source/generated_files", "FlavoredNeutrinoContainer.cpp_Renormalize_fill")) - # Write code to output file, using a template if one is provided - # write_code(code, "code.cpp", args.output_template) + # Store dN/dt and dNbar/dt set to zero + dNdt = HermitianMatrix(args.N, "p.rdata(PIdx::N{}{}_{}"+t+")") # Derivative of the neutrino number matrix + zero_matrix = HermitianMatrix(args.N, "0.0") # Zero matrix + dNdt.H = zero_matrix.H # Set the derivative of the neutrino number matrix to zero + # Write out dN/dt = 0 + code.append(dNdt.code()) - #====================================================# - # FlavoredNeutrinoContainerInit.cpp_set_trace_length # - #====================================================# - code = [] - for t in tails: - f = HermitianMatrix(args.N, "p.rdata(PIdx::f{}{}_{}"+t+")") - code.append("p.rdata(PIdx::L"+t+") = "+sympy.cxxcode(sympy.simplify(f.SU_vector_magnitude()))+";" ) - write_code(code, os.path.join(args.emu_home, "Source/generated_files/FlavoredNeutrinoContainerInit.cpp_set_trace_length")) + code = [line for sublist in code for line in sublist] + write_code(code, os.path.join(args.emu_home, "Source/generated_files", "Evolve.cpp_dfdt_fill_zeros")) \ No newline at end of file diff --git a/Scripts/tests/bc_empty_init_test.py b/Scripts/tests/bc_empty_init_test.py new file mode 100644 index 00000000..81af079b --- /dev/null +++ b/Scripts/tests/bc_empty_init_test.py @@ -0,0 +1,397 @@ +''' +This test script is used to check if the periodic empty boundary conditions are correctly implemented in the EMU code. +The periodic empty boundary conditions are implemented in the following way: +The particles in the boundary cells should be autamatically set to zero. +Created by Erick Urquilla. University of Tennessee Knoxville, USA. +''' + +import numpy as np +import h5py +import glob +import matplotlib.pyplot as plt +import argparse + +# Define myassert function +parser = argparse.ArgumentParser() +parser.add_argument("-na", "--no_assert", action="store_true", help="If --no_assert is supplied, do not raise assertion errors if the test error > tolerance.") +args = parser.parse_args() +def myassert(condition): + if not args.no_assert: + assert(condition) + +# physical constants +clight = 2.99792458e10 # cm/s +hbar = 1.05457266e-27 # erg s +h = 2.0*np.pi*hbar # erg s +eV = 1.60218e-12 # erg + +# Reading plt* directories +directories = np.array( glob.glob("plt*.h5") ) +# Extract directories with "old" in their names +mask = np.char.find(directories, "old") == -1 +directories = directories[mask] + +# Sort the data file names by time step number +directories = sorted(directories, key=lambda x: int(x.split("plt")[1].split(".")[0])) + +############################################################ +############################################################ +# PLOT SETTINGS +import matplotlib as mpl +from matplotlib.ticker import AutoLocator, AutoMinorLocator, LogLocator + +# Font settings +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +# mpl.rc('text', usetex=True) + +# Tick settings +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 + +# Axis linewidth +mpl.rcParams['axes.linewidth'] = 2 + +# Tick direction and enabling ticks on all sides +mpl.rcParams['xtick.direction'] = 'in' +mpl.rcParams['ytick.direction'] = 'in' +mpl.rcParams['xtick.top'] = True +mpl.rcParams['ytick.right'] = True + +# Function to apply custom tick locators and other settings to an Axes object +def apply_custom_settings(ax, leg, log_scale_y=False): + + if log_scale_y: + # Use LogLocator for the y-axis if it's in log scale + ax.set_yscale('log') + ax.yaxis.set_major_locator(LogLocator(base=10.0)) + ax.yaxis.set_minor_locator(LogLocator(base=10.0, subs='auto', numticks=100)) + else: + # Use AutoLocator for regular scales + ax.yaxis.set_major_locator(AutoLocator()) + ax.yaxis.set_minor_locator(AutoMinorLocator()) + + # Apply the AutoLocator for the x-axis + ax.xaxis.set_major_locator(AutoLocator()) + ax.xaxis.set_minor_locator(AutoMinorLocator()) + + # Legend settings + leg.get_frame().set_edgecolor('w') + leg.get_frame().set_linewidth(0.0) +############################################################ +############################################################ + +# Domain size in 3D index space +ncell = (5, 5, 5) +Lx = 5 # cm +Ly = 5 # cm +Lz = 5 # cm + +# Black hole parameters +bh_radius = 0.5 # cm +bh_center_x = 2.5 # cm +bh_center_y = 2.5 # cm +bh_center_z = 2.5 # cm + +# Contains mesh faces coordinates +cell_x_faces = np.linspace(0, Lx, ncell[0] + 1) +cell_y_faces = np.linspace(0, Ly, ncell[1] + 1) +cell_z_faces = np.linspace(0, Lz, ncell[2] + 1) + +all_files_ee_ocupation_in_each_cell = np.zeros((len(directories), *ncell)) # number of particles units +all_files_eebar_ocupation_in_each_cell = np.zeros((len(directories), *ncell)) # number of particles units +all_files_uu_ocupation_in_each_cell = np.zeros((len(directories), *ncell)) # number of particles units +all_files_uubar_ocupation_in_each_cell = np.zeros((len(directories), *ncell)) # number of particles units +time = np.zeros(len(directories)) # seconds + +x_pos_bh_cell = [] +y_pos_bh_cell = [] +z_pos_bh_cell = [] + +N00_Re_bh_cell = [] +N11_Re_bh_cell = [] +N00_Rebar_bh_cell = [] +N11_Rebar_bh_cell = [] + +# Looping over all directories +for i in range(len(directories)): + + # Print directory name + print(f'{directories[i]}') + + # Open file + with h5py.File(directories[i], 'r') as hf: + + N00_Re = np.array(hf['N00_Re']) # number of particles + N11_Re = np.array(hf['N11_Re']) # number of particles + N00_Rebar = np.array(hf['N00_Rebar']) # number of particles + N11_Rebar = np.array(hf['N11_Rebar']) # number of particles + E = np.array(hf['pupt']) # ergs + t = np.array(hf['time']) # seconds + Vphase = np.array(hf['Vphase']) # seconds + pos_x = np.array(hf['pos_x']) # cm + pos_y = np.array(hf['pos_y']) # cm + pos_z = np.array(hf['pos_z']) # cm + + # Append time + time[i] = t[0] + + # Shape n_particle x 3 array + # The first index runs over the particles + # The second index is the cell index in the x direction + # The third index is the cell index in the y direction + # The fourth index is the cell index in the z direction + particle_cell = np.zeros((len(N00_Re),3)) + + # Find index of cell in x direction + for j in range(ncell[0]): + mask = ( (pos_x > cell_x_faces[j]) & (pos_x < cell_x_faces[j+1]) ) + particle_cell[:,0][mask] = j + + # Find index of cell in y direction + for j in range(ncell[1]): + mask = ( (pos_y > cell_y_faces[j]) & (pos_y < cell_y_faces[j+1]) ) + particle_cell[:,1][mask] = j + + # Find index of cell in z direction + for j in range(ncell[2]): + mask = ( (pos_z > cell_z_faces[j]) & (pos_z < cell_z_faces[j+1]) ) + particle_cell[:,2][mask] = j + + # Initialize arrays to store occupation numbers for each cell + ee_ocupation_in_each_cell = np.zeros(ncell) + eebar_ocupation_in_each_cell = np.zeros(ncell) + uu_ocupation_in_each_cell = np.zeros(ncell) + uubar_ocupation_in_each_cell = np.zeros(ncell) + + # Loop over all cells in the x, y, and z directions + for j in range(ncell[0]): + for k in range(ncell[1]): + for l in range(ncell[2]): + # Create a mask to identify particles in the current cell (j, k, l) + mask = ( (particle_cell[:,0] == j) & (particle_cell[:,1] == k) & (particle_cell[:,2] == l) ) + + # Print the number of particles of type N00 in the current cell + print(f'Cell ({j},{k},{l}) : N00_Re = {np.sum(N00_Re[mask])}') + print(f'Cell ({j},{k},{l}) : N00_Rebar = {np.sum(N00_Rebar[mask])}') + print(f'Cell ({j},{k},{l}) : N11_Re = {np.sum(N11_Re[mask])}') + print(f'Cell ({j},{k},{l}) : N11_Rebar = {np.sum(N11_Rebar[mask])}') + + # Sum the number of particles of each type in the current cell and store in the respective arrays + ee_ocupation_in_each_cell[j,k,l] = np.sum(N00_Re[mask]) + eebar_ocupation_in_each_cell[j,k,l] = np.sum(N00_Rebar[mask]) + uu_ocupation_in_each_cell[j,k,l] = np.sum(N11_Re[mask]) + uubar_ocupation_in_each_cell[j,k,l] = np.sum(N11_Rebar[mask]) + + if j==2 and k==2 and l==2: + x_pos_bh_cell.append( pos_x[mask] ) + y_pos_bh_cell.append( pos_y[mask] ) + z_pos_bh_cell.append( pos_z[mask] ) + N00_Re_bh_cell.append( N00_Re[mask] ) + N11_Re_bh_cell.append( N11_Re[mask] ) + N00_Rebar_bh_cell.append( N00_Rebar[mask] ) + N11_Rebar_bh_cell.append( N11_Rebar[mask] ) + + # Store the occupation numbers for the current file in the all_files arrays + all_files_ee_ocupation_in_each_cell[i] = ee_ocupation_in_each_cell + all_files_eebar_ocupation_in_each_cell[i] = eebar_ocupation_in_each_cell + all_files_uu_ocupation_in_each_cell[i] = uu_ocupation_in_each_cell + all_files_uubar_ocupation_in_each_cell[i] = uubar_ocupation_in_each_cell + +# Theoretical values for the number of particles +N00_Re_theory = 3.0e+33 +N00_Rebar_theory = 2.5e+33 +N11_Re_theory = 1.0e+33 +N11_Rebar_theory = 1.0e+33 + +rel_error_max = 0.05 + +# Print the distance traveled by the particles +print(f'Distance traveled by particles = {time[-1]*clight} cm') + +# Loop over all cells in the x, y, and z directions +for i in range(ncell[0]): + for j in range(ncell[1]): + for k in range(ncell[2]): + + # Check if the cell is in the second layer next to the boundary + if (i > 0) and (i < ncell[0] - 1) and (j > 0) and (j < ncell[1] - 1) and (k > 0) and (k < ncell[2] - 1): + + # Calculate the relative errors for the central cells + if i == 2 and j == 2 and k == 2: + + # Calculate the distance of particles from the black hole center + particle_distance_from_bh_center = np.sqrt( + (np.array(x_pos_bh_cell[-1]) - bh_center_x)**2 + + (np.array(y_pos_bh_cell[-1]) - bh_center_y)**2 + + (np.array(z_pos_bh_cell[-1]) - bh_center_z)**2 + ) # cm + + # Mask for particles inside the black hole + mask = particle_distance_from_bh_center < bh_radius + particle_ins_bh = np.sum(N00_Re_bh_cell[-1][mask]) + + # Mask for particles outside the black hole + mask = particle_distance_from_bh_center > bh_radius + particle_out_bh = np.sum(N00_Re_bh_cell[-1][mask]) + + # Print the number of particles inside and outside the black hole + print(f'3BoundaryLayer: Cell ({i},{j},{k}) : Number of particles inside the black hole = {particle_ins_bh}') + print(f'3BoundaryLayer: Cell ({i},{j},{k}) : Number of particles outside the black hole = {particle_out_bh}') + + # Assert that the number of particles inside the black hole is less than the maximum relative error + myassert(particle_ins_bh < rel_error_max) + + else: + + # Calculate relative errors for ee and eebar occupation numbers + rel_error_ee = np.abs(all_files_ee_ocupation_in_each_cell[-1, i, j, k] - N00_Re_theory) / N00_Re_theory + rel_error_eebar = np.abs(all_files_eebar_ocupation_in_each_cell[-1, i, j, k] - N00_Rebar_theory) / N00_Rebar_theory + + # Print the relative errors for ee and eebar occupation numbers + print(f"2BoundaryLayer: Cell ({i},{j},{k}) : relative error in ee = {rel_error_ee}") + print(f"2BoundaryLayer: Cell ({i},{j},{k}) : relative error in eebar = {rel_error_eebar}") + + # Assert that the relative errors are less than the maximum relative error + myassert(rel_error_ee < rel_error_max) + myassert(rel_error_eebar < rel_error_max) + + else: + + # Calculate the relative errors for the boundary cells + rel_error_ee = np.abs(all_files_ee_ocupation_in_each_cell[-1, i, j, k]) + rel_error_eebar = np.abs(all_files_eebar_ocupation_in_each_cell[-1, i, j, k]) + + # Print the relative errors for the boundary cells + print(f"1BoundaryLayer: Cell ({i},{j},{k}) : relative error in ee = {rel_error_ee}") + print(f"1BoundaryLayer: Cell ({i},{j},{k}) : relative error in eebar = {rel_error_eebar}") + + # Assert that the relative errors are less than the maximum relative error + myassert(rel_error_ee < rel_error_max) + myassert(rel_error_eebar < rel_error_max) + +########################################################################################## +# PLOTTING +########################################################################################## + +# Create a figure and axis for plotting electron occupation numbers +fig1, ax1 = plt.subplots() + +# Loop over all cells in the x, y, and z directions +for i in range(ncell[0]): + for j in range(ncell[1]): + for k in range(ncell[2]): + + # Check if the cell is in the second layer next to the boundary + if (i > 0) and (i < ncell[0] - 1) and (j > 0) and (j < ncell[1] - 1) and (k > 0) and (k < ncell[2] - 1): + + # Plot the central cells + if i == 2 and j == 2 and k == 2: + + particle_ins_bh_time = [] + particle_out_bh_time = [] + + for l in range(len(directories)): + + # Calculate the distance of particles from the black hole center + particle_distance_from_bh_center = np.sqrt( + (np.array(x_pos_bh_cell[l]) - bh_center_x)**2 + + (np.array(y_pos_bh_cell[l]) - bh_center_y)**2 + + (np.array(z_pos_bh_cell[l]) - bh_center_z)**2 + ) # cm + + # Mask for particles inside the black hole + mask = particle_distance_from_bh_center < bh_radius + particle_ins_bh_time.append(np.sum(N00_Re_bh_cell[l][mask])) + + # Mask for particles outside the black hole + mask = particle_distance_from_bh_center > bh_radius + particle_out_bh_time.append(np.sum(N00_Re_bh_cell[l][mask])) + + ax1.plot(time, particle_ins_bh_time, label=f'Particles inside BH', linestyle='solid', color='black') + ax1.plot(time, particle_out_bh_time, label=f"Particles ouside BH but in BH's cell", linestyle='solid', color='gray') + + else: + # Plot cells adjacent to boundary cell + ax1.plot(time, all_files_ee_ocupation_in_each_cell[:, i, j, k], linestyle='dashed', color='orange') + else: + # Plot boundary cells + ax1.plot(time, all_files_ee_ocupation_in_each_cell[:, i, j, k], linestyle='dotted', color='red') + +# Set the x and y axis labels +ax1.set_xlabel('time ($s$)') +ax1.set_ylabel('$N_{ee}$') + +# Add a legend to the plot +leg1 = ax1.legend(framealpha=0.0, ncol=3, fontsize=10) + +# Apply custom settings to the plot +apply_custom_settings(ax1, leg1, False) + +# Adjust layout and save the figure +plt.tight_layout() +fig1.savefig('electron_occupation.pdf', bbox_inches='tight') + +# Create a figure and axis for plotting electron occupation numbers (bar) +fig2, ax2 = plt.subplots() + +# Loop over all cells in the x, y, and z directions +for i in range(ncell[0]): + for j in range(ncell[1]): + for k in range(ncell[2]): + + # Check if the cell is in the second layer next to the boundary + if (i > 0) and (i < ncell[0] - 1) and (j > 0) and (j < ncell[1] - 1) and (k > 0) and (k < ncell[2] - 1): + + # Plot the central cells + if i == 2 and j == 2 and k == 2: + + particle_ins_bh_time = [] + particle_out_bh_time = [] + + for l in range(len(directories)): + + # Calculate the distance of particles from the black hole center + particle_distance_from_bh_center = np.sqrt( + (np.array(x_pos_bh_cell[l]) - bh_center_x)**2 + + (np.array(y_pos_bh_cell[l]) - bh_center_y)**2 + + (np.array(z_pos_bh_cell[l]) - bh_center_z)**2 + ) # cm + + # Mask for particles inside the black hole + mask = particle_distance_from_bh_center < bh_radius + particle_ins_bh_time.append(np.sum(N00_Rebar_bh_cell[l][mask])) + + # Mask for particles outside the black hole + mask = particle_distance_from_bh_center > bh_radius + particle_out_bh_time.append(np.sum(N00_Rebar_bh_cell[l][mask])) + + ax2.plot(time, particle_ins_bh_time, label=f'Particles inside BH', linestyle='solid', color='black') + ax2.plot(time, particle_out_bh_time, label=f"Particles ouside BH but in BH's cell", linestyle='solid', color='gray') + + else: + # Plot cells adjacent to boundary cell + ax2.plot(time, all_files_eebar_ocupation_in_each_cell[:, i, j, k], linestyle='dashed', color='orange') + else: + # Plot boundary cells + ax2.plot(time, all_files_eebar_ocupation_in_each_cell[:, i, j, k], linestyle='dotted', color='red') + +# Set the x and y axis labels +ax2.set_xlabel('time ($s$)') +ax2.set_ylabel('$\\bar{N}_{ee}$') + +# Add a legend to the plot +leg2 = ax2.legend(framealpha=0.0, ncol=3, fontsize=10) + +# Apply custom settings to the plot +apply_custom_settings(ax2, leg2, False) + +# Adjust layout and save the figure +plt.tight_layout() +fig2.savefig('electron_occupation_bar.pdf', bbox_inches='tight') diff --git a/Scripts/tests/coll_equi_test.py b/Scripts/tests/coll_equi_test.py new file mode 100644 index 00000000..b2e3145d --- /dev/null +++ b/Scripts/tests/coll_equi_test.py @@ -0,0 +1,61 @@ +''' +This script tests the equilibrium value of the collision term. +Created by Erick Urquilla, University of Tennessee, Knoxville. +''' +import numpy as np +import argparse +import glob +import EmuReader +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__))+"/../data_reduction/" +sys.path.append(importpath) +import amrex_plot_tools as amrex + +parser = argparse.ArgumentParser() +parser.add_argument("-na", "--no_assert", action="store_true", help="If --no_assert is supplied, do not raise assertion errors if the test error > tolerance.") +args = parser.parse_args() + +NF=3 + +if __name__ == "__main__": + + rkey, ikey = amrex.get_particle_keys(NF) + + N_ee = [] + N_uu = [] + N_tt = [] + N_eebar = [] + N_uubar = [] + N_ttbar = [] + + idata, rdata = EmuReader.read_particle_data('plt01000', ptype="neutrinos") + + for i in range(len(rdata)): + p = rdata[i] + N_ee.append(p[rkey["N00_Re"]]) + N_uu.append(p[rkey["N11_Re"]]) + N_tt.append(p[rkey["N22_Re"]]) + N_eebar.append(p[rkey["N00_Rebar"]]) + N_uubar.append(p[rkey["N11_Rebar"]]) + N_ttbar.append(p[rkey["N22_Rebar"]]) + + print(f'The expected theoretical value for the following quantities is 1e33.') + print(f'The numerical values obtained using EMU are:') + print(f'average N_ee {np.average(N_ee)}') + print(f'average N_uu {np.average(N_uu)}') + print(f'average N_tt {np.average(N_tt)}') + print(f'average N_eebar {np.average(N_eebar)}') + print(f'average N_uubar {np.average(N_uubar)}') + print(f'average N_ttbar {np.average(N_ttbar)}') + + def myassert(condition): + if not args.no_assert: + assert(condition) + + myassert( np.all(np.isclose(N_ee, np.array(1e33), atol=1e33/100)) ) + myassert( np.all(np.isclose(N_uu, np.array(1e33), atol=1e33/100)) ) + myassert( np.all(np.isclose(N_tt, np.array(1e33), atol=1e33/100)) ) + myassert( np.all(np.isclose(N_eebar, np.array(1e33), atol=1e33/100)) ) + myassert( np.all(np.isclose(N_uubar, np.array(1e33), atol=1e33/100)) ) + myassert( np.all(np.isclose(N_ttbar, np.array(1e33), atol=1e33/100)) ) \ No newline at end of file diff --git a/Scripts/tests/coll_inst_test.py b/Scripts/tests/coll_inst_test.py new file mode 100644 index 00000000..4d22f404 --- /dev/null +++ b/Scripts/tests/coll_inst_test.py @@ -0,0 +1,276 @@ +''' +This test script is used to reproduce the isotropic 2-flavor simulation in "Collisional Flavor Instabilities of Supernova Neutrinos" by L. Johns [2104.11369]. +The points of comparison are the LSA conducted in this paper (equation 14) and Julien's script that reproduces the same results (script received via private communication). +Created by Erick Urquilla. University of Tennessee Knoxville, USA. +''' + +import numpy as np +import argparse +import glob +import EmuReader +import sys +import os +importpath = os.path.dirname(os.path.realpath(__file__))+"/../data_reduction/" +sys.path.append(importpath) +import amrex_plot_tools as amrex +import numpy as np +import h5py +import glob + +parser = argparse.ArgumentParser() +parser.add_argument("-na", "--no_assert", action="store_true", help="If --no_assert is supplied, do not raise assertion errors if the test error > tolerance.") +args = parser.parse_args() + +if __name__ == "__main__": + + # Create a list of data files to read + directories = glob.glob("plt*_reduced_data.h5") + # Sort the data file names by time step number + directories = sorted(directories, key=lambda x: int(x.split("plt")[1].split("_")[0])) + + N_avg_mag = np.zeros((len(directories),2,2)) + Nbar_avg_mag = np.zeros((len(directories),2,2)) + F_avg_mag = np.zeros((len(directories),3,2,2)) + Fbar_avg_mag = np.zeros((len(directories),3,2,2)) + t = np.zeros(len(directories)) + + for i in range(len(directories)): + with h5py.File(directories[i], 'r') as hf: + N_avg_mag[i] = np.array(hf['N_avg_mag(1|ccm)'][:][0]) + Nbar_avg_mag[i] = np.array(hf['Nbar_avg_mag(1|ccm)'][:][0]) + F_avg_mag[i] = np.array(hf['F_avg_mag(1|ccm)'][:][0]) + Fbar_avg_mag[i] = np.array(hf['Fbar_avg_mag(1|ccm)'][:][0]) + t[i] = np.array(hf['t(s)'][:][0]) + + # Fit the exponential function ( y = a e ^ ( b x ) ) to the data + l1 = 10 # initial item for fit + l2 = 40 # last item for fit + coefficients = np.polyfit(t[l1:l2], np.log(N_avg_mag[:,0,1][l1:l2]), 1) + coefficients_bar = np.polyfit(t[l1:l2], np.log(Nbar_avg_mag[:,0,1][l1:l2]), 1) + a = np.exp(coefficients[1]) + b = coefficients[0] + abar = np.exp(coefficients_bar[1]) + bbar = coefficients_bar[0] + print(f'{b} ---> EMU : Im Omega') + print(f'{bbar} ---> EMU : Im Omegabar') + + # Plots + import matplotlib.pyplot as plt + + # Plots N and Nbar + plt.plot(t, N_avg_mag[:,0,0], label = r'$N_{ee}$') + plt.plot(t, N_avg_mag[:,0,1], label = r'$N_{eu}$') + plt.plot(t, N_avg_mag[:,1,1], label = r'$N_{uu}$') + plt.plot(t, Nbar_avg_mag[:,0,0], label = r'$\bar{N}_{ee}$') + plt.plot(t, Nbar_avg_mag[:,0,1], label = r'$\bar{N}_{eu}$') + plt.plot(t, Nbar_avg_mag[:,1,1], label = r'$\bar{N}_{uu}$') + plt.legend() + plt.xlabel(r'$t$ (s)') + plt.ylabel(r'$N$ and $\bar{N}$') + plt.savefig('N_and_Nbar.pdf') + plt.clf() + + # Plots N and F + plt.plot(t, N_avg_mag[:,0,0], label = r'$N_{ee}$') + plt.plot(t, N_avg_mag[:,0,1], label = r'$N_{eu}$') + plt.plot(t, N_avg_mag[:,1,1], label = r'$N_{uu}$') + plt.plot(t[l1:l2], N_avg_mag[:,0,1][l1:l2], label = f'Im Omega = {b}') + plt.plot(t, F_avg_mag[:,0,0,1], label = r'$F^x_{eu}$') + plt.plot(t, F_avg_mag[:,1,0,1], label = r'$F^y_{eu}$') + plt.plot(t, F_avg_mag[:,2,0,1], label = r'$F^z_{eu}$') + plt.legend() + plt.xlabel(r'$t$ (s)') + plt.ylabel(r'$N$ and $\vec{F}$') + plt.yscale('log') + plt.savefig('N_and_F.pdf') + plt.clf() + + # Plots Nbar and Fbar + plt.plot(t, Nbar_avg_mag[:,0,0], label = r'$\bar{N}_{ee}$') + plt.plot(t, Nbar_avg_mag[:,0,1], label = r'$\bar{N}_{eu}$') + plt.plot(t, Nbar_avg_mag[:,1,1], label = r'$\bar{N}_{uu}$') + plt.plot(t[l1:l2], Nbar_avg_mag[:,0,1][l1:l2], label = f'Im Omega = {bbar}') + plt.plot(t, Fbar_avg_mag[:,0,0,1], label = r'$\bar{F}^x_{eu}$') + plt.plot(t, Fbar_avg_mag[:,1,0,1], label = r'$\bar{F}^y_{eu}$') + plt.plot(t, Fbar_avg_mag[:,2,0,1], label = r'$\bar{F}^z_{eu}$') + plt.legend() + plt.xlabel(r'$t$ (s)') + plt.ylabel(r'$\bar{N}$ and $\vec{\bar{F}}$') + plt.yscale('log') + plt.savefig('Nbar_and_Fbar.pdf') + plt.clf() + + ###################################################################################### + ###################################################################################### + # LSA in "Collisional flavor instabilities of supernova neutrinos", L. Johns [2104.11369] + + h = 6.6260755e-27 # erg s + hbar = h/(2.*np.pi) # erg s + c = 2.99792458e10 # cm/s + MeV = 1.60218e-6 # erg + eV = MeV/1e6 # erg + GF_GeV2 = 1.1663787e-5 # GeV^-2 + GF = GF_GeV2 / (1000*MeV)**2 * (hbar*c)**3 # erg cm^3 + + Nee = 3e33 # cm^-3 + Neebar = 2.5e33 # cm^-3 + Nxx = 1e33 # cm^-3 + + opac_rescale = 1e4 + + kappa_e = 1/(0.417*1e5)*opac_rescale # cm^-1 + kappa_ebar = 1/(4.36*1e5)*opac_rescale # cm^-1 + kappa_x = 0.*opac_rescale # cm^-1 + + # Collision rates (in s^-1) + Gamma_plus = (kappa_e+kappa_x)/2 * c + Gamma_minus = (kappa_e-kappa_x)/2 * c + Gammabar_plus = (kappa_ebar+kappa_x)/2 * c + Gammabar_minus= (kappa_ebar - kappa_x)/2 * c + + omega = 0.304*1e-5 * c # Delta m^2/2E, in s^-1 + mu = np.sqrt(2)*GF/hbar # s^-1.cm^3 + + S = Nee - Nxx + Neebar - Nxx + D = Nee - Nxx - Neebar + Nxx + + ImOmega_Lucas_LSA = ( ( Gamma_plus - Gammabar_plus ) / 2 ) * ( mu * S / np.sqrt( ( mu * D )**2 + 4 * omega * mu * S ) ) - ( Gamma_plus + Gammabar_plus ) / 2 + + print(f'{ImOmega_Lucas_LSA} ---> Im Omega and Omegabar : LSA in equation 14 of L. Johns [2104.11369]') + + ###################################################################################### + ###################################################################################### + + def myassert(condition): + if not args.no_assert: + assert(condition) + + b_lsa = ImOmega_Lucas_LSA + rel_error = np.abs( b - b_lsa ) / np.abs( ( b + b_lsa ) / 2 ) + rel_error_bar = np.abs( bbar - b_lsa ) / np.abs( ( bbar + b_lsa ) / 2 ) + rel_error_max = 0.05 + + print(f"{rel_error} ---> relative error in ImOmega : EMU") + print(f"{rel_error_bar} ---> relative error in ImOmegabar : EMU") + + myassert( rel_error < rel_error_max ) + myassert( rel_error_bar < rel_error_max ) + + ###################################################################################### + ###################################################################################### + + """ + Created on Wed Jun 5 13:11:50 2024 + Solves the isotropic QKE following "Collisional flavor instabilities of supernova neutrinos", L. Johns [2104.11369] + + @author: jfroustey + """ + + import numpy as np + import matplotlib.pyplot as plt + from scipy.integrate import solve_ivp + + h = 6.6260755e-27 # erg s + hbar = h/(2.*np.pi) # erg s + c = 2.99792458e10 # cm/s + MeV = 1.60218e-6 # erg + eV = MeV/1e6 # erg + GF_GeV2 = 1.1663787e-5 # GeV^-2 + GF = GF_GeV2 / (1000*MeV)**2 * (hbar*c)**3 # erg cm^3 + + Nee = 3e33 # cm^-3 + Neebar = 2.5e33 # cm^-3 + Nxx = 1e33 # cm^-3 + + opac_rescale = 1e4 + + kappa_e = 1/(0.417*1e5)*opac_rescale # cm^-1 + kappa_ebar = 1/(4.36*1e5)*opac_rescale # cm^-1 + kappa_x = 0.*opac_rescale # cm^-1 + + # Collision rates (in s^-1) + Gamma_plus = (kappa_e+kappa_x)/2 * c + Gamma_minus = (kappa_e-kappa_x)/2 * c + Gammabar_plus = (kappa_ebar+kappa_x)/2 * c + Gammabar_minus= (kappa_ebar - kappa_x)/2 * c + + # Vacuum term + + theta = 1e-6 + c2t = np.cos(2*theta) + s2t = np.sin(2*theta) + omega = 0.304*1e-5 * c # Delta m^2/2E, in s^-1 + + P0_AE = (Nee+Nxx)/2 + Pz_AE = (Nee-Nxx)/2 + Pbar0_AE = (Neebar+Nxx)/2 + Pbarz_AE = (Neebar-Nxx)/2 + + mu = np.sqrt(2)*GF/hbar # s^-1.cm^3 + + def QKE(t,y): + P0, Px, Py, Pz, Pbar0, Pbarx, Pbary, Pbarz = y + deriv = np.zeros(8) + + # Variation of P0, Pbar0 + deriv[0] = Gamma_plus*(P0_AE-P0) + Gamma_minus*(Pz_AE-Pz) + deriv[4] = Gammabar_plus*(Pbar0_AE-Pbar0) + Gammabar_minus*(Pbarz_AE - Pbarz) + + # Spatial parts + deriv[1] = omega*c2t*Py + mu*((Py-Pbary)*Pz - (Pz-Pbarz)*Py) - Gamma_plus*Px + deriv[2] = omega*(-s2t*Pz-c2t*Px) + mu*((Pz-Pbarz)*Px - (Px-Pbarx)*Pz) - Gamma_plus*Py + deriv[3] = omega*s2t*Py + mu*((Px-Pbarx)*Py - (Py-Pbary)*Px) + Gamma_plus*(Pz_AE-Pz) + Gamma_minus*(P0_AE-P0) + + deriv[5] = -omega*c2t*Pbary + mu*((Py-Pbary)*Pbarz - (Pz-Pbarz)*Pbary) - Gammabar_plus*Pbarx + deriv[6] = -omega*(-s2t*Pbarz - c2t*Pbarx) + mu*((Pz-Pbarz)*Pbarx - (Px-Pbarx)*Pbarz) - Gammabar_plus*Pbary + deriv[7] = -omega*s2t*Pbary + mu*((Px-Pbarx)*Pbary - (Py-Pbary)*Pbarx) + Gammabar_plus*(Pbarz_AE-Pbarz) + Gammabar_minus*(Pbar0_AE-Pbar0) + + return deriv + + time = np.linspace(0,90e-6/opac_rescale,2000) + y0 = np.array([P0_AE, 0., 0., Pz_AE, Pbar0_AE, 0., 0., Pbarz_AE]) + + myrtol, myatol = 1e-5, 1e-8 + sol = solve_ivp(QKE, (time[0],time[-1]), y0, t_eval=time, rtol=myrtol, atol=myatol) + + # PLOTS + plt.plot(time, sol.y[0,:]+sol.y[3,:], color='k', lw=2, label=r'$n_{\nu_e}$') + plt.plot(time, sol.y[4,:]+sol.y[7,:], color='k', lw=1.5, label=r'$n_{\bar{\nu}_e}$') + plt.plot(time, sol.y[0,:]-sol.y[3,:], color='k', lw=1, label=r'$n_{\nu_x}$') + + plt.plot(time,np.sqrt(sol.y[1,:]**2+sol.y[2,:]**2), lw=2, color='teal',label=r'$\nu_e - \nu_x$'+" coherence density") + plt.legend() + plt.grid(ls=':',color='C7') + plt.xlabel(r'$t \ (\mathrm{s})$') + plt.xlim(time[0],time[-1]) + plt.title(f"Opacities scaled by {opac_rescale:.1e}, with rtol={myrtol:.1e}, atol={myatol:.1e}") + plt.tight_layout() + plt.savefig(f"Johns_CFI_rescale_{opac_rescale:.0e}_rtol_{myrtol:.0e}_atol_{myatol:.0e}.pdf") + plt.close() + + ###################################################################################### + ###################################################################################### + + p1 = 150 # initial point for fit + p2 = 500 # final point for fit + N_eu_julien = np.sqrt(sol.y[1,:]**2+sol.y[2,:]**2) + coefficients = np.polyfit(time[p1:p2], np.log(N_eu_julien[p1:p2]), 1) + aj = np.exp(coefficients[1]) + bj = coefficients[0] + print(f'{bj} ---> Im Omega Julien') + rel_error_j = np.abs( bj - b_lsa ) / np.abs( ( bj + b_lsa ) / 2 ) + print(f"{rel_error_j} ---> relative erroror in Julien script") + + # Plotting Julien, EMU and Lucas LSA data + plt.plot(t, N_avg_mag[:,0,1], label = r'$N_{eu}$ EMU') + plt.plot(t[l1:l2], N_avg_mag[:,0,1][l1:l2], label = f'Im Omega EMU = {b}', linestyle = 'dashed') + plt.plot(time,N_eu_julien, label = r'$N_{eu}$ Julien script') + plt.plot(time[p1:p2],N_eu_julien[p1:p2], label = f'Im Omega Julien = {bj}', linestyle = 'dashed') + plt.plot(time[p1:p2], 1e23*np.exp(ImOmega_Lucas_LSA*time[p1:p2]), label = f'Im Omega Lucas LSA = {ImOmega_Lucas_LSA}', linestyle = 'dashed') + plt.xlabel(r'$t \ (\mathrm{s})$') + plt.ylabel(r'$N_{eu}$') + plt.title(f"Collisional flavor instability test") + plt.yscale('log') + plt.legend() + plt.savefig('EMU_Julien_LucasLSA_Neu.pdf') + plt.close() diff --git a/Scripts/tests/convergence.sh b/Scripts/tests/convergence.sh deleted file mode 100644 index 3816a379..00000000 --- a/Scripts/tests/convergence.sh +++ /dev/null @@ -1,89 +0,0 @@ -#!/bin/bash - -# echo the commands -set -x - -# All runs will use these -DIM=3 -EXEC=./main${DIM}d.gnu.TPROF.MPI.ex -MPINUM=4 - -RUNPARAMS=" -cfl_factor=-1 -nsteps=1000000 -end_time=5.0e-11" - -# Each integrator will set these -INTPARAMS="" -INTNAME="" - -# Clean up any existing output files before we start -rm -rf plt* -rm -rf single_neutrino*.png -rm -rf msw_test_*.txt - -# Define a function for a single run -do_single () { - mpiexec -n ${MPINUM} ${EXEC} inputs_msw_test ${RUNPARAMS} flavor_cfl_factor=${FCFL} ${INTPARAMS} - echo "cfl: ${FCFL}" >> msw_test_${INTNAME}.txt - python3 msw_test.py -na >> msw_test_${INTNAME}.txt - python3 plot_first_particle.py - mv single_neutrino.png single_neutrino_fcfl_${FCFL}_${INTNAME}.png - rm -rf plt* -} - -# Define a function for running convergence -do_convergence () { - FCFL=0.1 - do_single - - FCFL=0.05 - do_single - - FCFL=0.025 - do_single - - FCFL=0.0125 - do_single - - FCFL=0.00625 - do_single - - FCFL=0.003125 - do_single -} - -# Forward Euler convergence -INTPARAMS=" -integration.type=0" - -INTNAME="fe" - -do_convergence - -# Trapezoid convergence -INTPARAMS=" -integration.type=1 -integration.rk.type=2" - -INTNAME="trapz" - -do_convergence - -# SSPRK3 Convergence -INTPARAMS=" -integration.type=1 -integration.rk.type=3" - -INTNAME="ssprk3" - -do_convergence - -# RK4 Convergence -INTPARAMS=" -integration.type=1 -integration.rk.type=4" - -INTNAME="rk4" - -do_convergence diff --git a/Scripts/tests/fast_flavor_k_test.py b/Scripts/tests/fast_flavor_k_test.py index 31562870..8b661152 100644 --- a/Scripts/tests/fast_flavor_k_test.py +++ b/Scripts/tests/fast_flavor_k_test.py @@ -4,7 +4,7 @@ import EmuReader import sys import os -importpath = os.path.dirname(os.path.realpath(__file__))+"/../visualization/" +importpath = os.path.dirname(os.path.realpath(__file__))+"/../data_reduction/" sys.path.append(importpath) import amrex_plot_tools as amrex @@ -17,6 +17,7 @@ tolerance = 1e-2 i0 = 100 i1 = 160 +NF = 2 # get domain size file = open("../sample_inputs/inputs_fast_flavor_nonzerok","r") @@ -26,13 +27,13 @@ if __name__ == "__main__": - rkey, ikey = amrex.get_particle_keys() + rkey, ikey = amrex.get_particle_keys(NF) t = [] - fexR = [] - fexI = [] - fexRbar = [] - fexIbar = [] + NexR = [] + NexI = [] + NexRbar = [] + NexIbar = [] pupt = [] nfiles = len(glob.glob("plt[0-9][0-9][0-9][0-9][0-9]")) @@ -42,18 +43,18 @@ idata, rdata = EmuReader.read_particle_data(plotfile, ptype="neutrinos") p = rdata t.append(p[0][rkey["time"]]) - fexR.append(np.max(np.abs(p[:,rkey["f01_Re"]]))) - fexI.append(np.max(np.abs(p[:,rkey["f01_Im"]]))) - fexRbar.append(np.max(np.abs(p[:,rkey["f01_Rebar"]]))) - fexIbar.append(np.max(np.abs(p[:,rkey["f01_Imbar"]]))) + NexR.append(np.max(np.abs(p[:,rkey["N01_Re"]]))) + NexI.append(np.max(np.abs(p[:,rkey["N01_Im"]]))) + NexRbar.append(np.max(np.abs(p[:,rkey["N01_Rebar"]]))) + NexIbar.append(np.max(np.abs(p[:,rkey["N01_Imbar"]]))) pupt.append(p[0][rkey["pupt"]]) t = np.array(t) - fexR = np.array(fexR) - fexI = np.array(fexI) - fexRbar = np.array(fexRbar) - fexIbar = np.array(fexIbar) - print(fexR) + NexR = np.array(NexR) + NexI = np.array(NexI) + NexRbar = np.array(NexRbar) + NexIbar = np.array(NexIbar) + print(NexR) # The neutrino energy we set E = 50. * 1e6*amrex.eV @@ -69,27 +70,27 @@ # get growth rate from each diagonal component dt = t[i1]-t[i0] - fexRomega = np.log(np.abs(fexR[i1]/fexR[i0])) / dt - fexIomega = np.log(np.abs(fexI[i1]/fexI[i0])) / dt - fexRbaromega = np.log(np.abs(fexRbar[i1]/fexRbar[i0])) / dt - fexIbaromega = np.log(np.abs(fexIbar[i1]/fexIbar[i0])) / dt + NexRomega = np.log(np.abs(NexR[i1]/NexR[i0])) / dt + NexIomega = np.log(np.abs(NexI[i1]/NexI[i0])) / dt + NexRbaromega = np.log(np.abs(NexRbar[i1]/NexRbar[i0])) / dt + NexIbaromega = np.log(np.abs(NexIbar[i1]/NexIbar[i0])) / dt - print("growth rates:",fexRomega,fexIomega,fexRbaromega,fexIbaromega) - print("growth rates / theoretical:",fexRomega/ImOmega,fexIomega/ImOmega,fexRbaromega/ImOmega,fexIbaromega/ImOmega) + print("growth rates:",NexRomega,NexIomega,NexRbaromega,NexIbaromega) + print("growth rates / theoretical:",NexRomega/ImOmega,NexIomega/ImOmega,NexRbaromega/ImOmega,NexIbaromega/ImOmega) def myassert(condition): if not args.no_assert: assert(condition) - fexRerror = np.abs(ImOmega - fexRomega) / ImOmega - myassert( fexRerror < tolerance ) + NexRerror = np.abs(ImOmega - NexRomega) / ImOmega + myassert( NexRerror < tolerance ) - fexIerror = np.abs(ImOmega - fexIomega) / ImOmega - myassert( fexIerror < tolerance ) + NexIerror = np.abs(ImOmega - NexIomega) / ImOmega + myassert( NexIerror < tolerance ) - fexRbarerror = np.abs(ImOmega - fexRbaromega) / ImOmega - myassert( fexRbarerror < tolerance ) + NexRbarerror = np.abs(ImOmega - NexRbaromega) / ImOmega + myassert( NexRbarerror < tolerance ) - fexIbarerror = np.abs(ImOmega - fexIbaromega) / ImOmega - myassert( fexIbarerror < tolerance ) + NexIbarerror = np.abs(ImOmega - NexIbaromega) / ImOmega + myassert( NexIbarerror < tolerance ) diff --git a/Scripts/tests/fast_flavor_test.py b/Scripts/tests/fast_flavor_test.py index 86c6e8d5..cd50fd75 100644 --- a/Scripts/tests/fast_flavor_test.py +++ b/Scripts/tests/fast_flavor_test.py @@ -4,7 +4,7 @@ import EmuReader import sys import os -importpath = os.path.dirname(os.path.realpath(__file__))+"/../visualization/" +importpath = os.path.dirname(os.path.realpath(__file__))+"/../data_reduction/" sys.path.append(importpath) import amrex_plot_tools as amrex @@ -19,16 +19,17 @@ tolerance = 2e-2 i0 = 50 i1 = 70 +NF=2 if __name__ == "__main__": - rkey, ikey = amrex.get_particle_keys() + rkey, ikey = amrex.get_particle_keys(NF) t = [] - fexR = [] - fexI = [] - fexRbar = [] - fexIbar = [] + NexR = [] + NexI = [] + NexRbar = [] + NexIbar = [] pupt = [] nfiles = len(glob.glob("plt[0-9][0-9][0-9][0-9][0-9]")) @@ -38,17 +39,18 @@ idata, rdata = EmuReader.read_particle_data(plotfile, ptype="neutrinos") p = rdata[0] t.append(p[rkey["time"]]) - fexR.append(p[rkey["f01_Re"]]) - fexI.append(p[rkey["f01_Im"]]) - fexRbar.append(p[rkey["f01_Rebar"]]) - fexIbar.append(p[rkey["f01_Imbar"]]) + NexR.append(p[rkey["N01_Re"]]) + NexI.append(p[rkey["N01_Im"]]) + p = rdata[1] + NexRbar.append(p[rkey["N01_Rebar"]]) + NexIbar.append(p[rkey["N01_Imbar"]]) pupt.append(p[rkey["pupt"]]) t = np.array(t) - fexR = np.array(fexR) - fexI = np.array(fexI) - fexRbar = np.array(fexRbar) - fexIbar = np.array(fexIbar) + NexR = np.array(NexR) + NexI = np.array(NexI) + NexRbar = np.array(NexRbar) + NexIbar = np.array(NexIbar) # The neutrino energy we set E = 50. * 1e6*amrex.eV @@ -60,32 +62,32 @@ # get growth rate from each diagonal component dt = t[i1]-t[i0] - fexRomega = np.log(fexR[i1]/fexR[i0]) / dt - fexIomega = np.log(fexI[i1]/fexI[i0]) / dt - fexRbaromega = np.log(fexRbar[i1]/fexRbar[i0]) / dt - fexIbaromega = np.log(fexIbar[i1]/fexIbar[i0]) / dt + NexRomega = np.log(NexR[i1]/NexR[i0]) / dt + NexIomega = np.log(NexI[i1]/NexI[i0]) / dt + NexRbaromega = np.log(NexRbar[i1]/NexRbar[i0]) / dt + NexIbaromega = np.log(NexIbar[i1]/NexIbar[i0]) / dt def myassert(condition): if not args.no_assert: assert(condition) - print("growth rates:",fexRomega,fexIomega,fexRbaromega,fexIbaromega) + print("growth rates:",NexRomega,NexIomega,NexRbaromega,NexIbaromega) print(dt,t[i0],t[i1]) - print(fexR[i1],fexR[i0]) - print(fexI[i1],fexI[i0]) - print(fexRbar[i1],fexRbar[i0]) - print(fexIbar[i1],fexIbar[i0]) + print(NexR[i1],NexR[i0]) + print(NexI[i1],NexI[i0]) + print(NexRbar[i1],NexRbar[i0]) + print(NexIbar[i1],NexIbar[i0]) - fexRerror = np.abs(ImOmega - fexRomega) / ImOmega - myassert( fexRerror < tolerance ) + NexRerror = np.abs(ImOmega - NexRomega) / ImOmega + myassert( NexRerror < tolerance ) - fexIerror = np.abs(ImOmega - fexIomega) / ImOmega - myassert( fexIerror < tolerance ) + NexIerror = np.abs(ImOmega - NexIomega) / ImOmega + myassert( NexIerror < tolerance ) - fexRbarerror = np.abs(ImOmega - fexRbaromega) / ImOmega - myassert( fexRbarerror < tolerance ) + NexRbarerror = np.abs(ImOmega - NexRbaromega) / ImOmega + myassert( NexRbarerror < tolerance ) - fexIbarerror = np.abs(ImOmega - fexIbaromega) / ImOmega - myassert( fexIbarerror < tolerance ) + NexIbarerror = np.abs(ImOmega - NexIbaromega) / ImOmega + myassert( NexIbarerror < tolerance ) diff --git a/Scripts/tests/fermi_dirac_test.py b/Scripts/tests/fermi_dirac_test.py new file mode 100644 index 00000000..ad7a5206 --- /dev/null +++ b/Scripts/tests/fermi_dirac_test.py @@ -0,0 +1,297 @@ +import numpy as np +import h5py +import glob +import matplotlib.pyplot as plt + +# physical constants +clight = 2.99792458e10 # cm/s +hbar = 1.05457266e-27 # erg s +h = 2.0*np.pi*hbar # erg s +eV = 1.60218e-12 # erg + +# Reading plt* directories +directories = np.array( glob.glob("plt*.h5") ) +# Extract directories with "old" in their names +mask = np.char.find(directories, "old") == -1 +directories = directories[mask] + +# Sort the data file names by time step number +directories = sorted(directories, key=lambda x: int(x.split("plt")[1].split(".")[0])) + +# Energy bin centers extracted from NuLib table +energies_center_Mev = np.array([1, 3, 5.23824, 8.00974, 11.4415, 15.6909, 20.9527, 27.4681, 35.5357, 45.5254, 57.8951, 73.2117, 92.1775, 115.662, 144.741, 180.748, 225.334, 280.542]) # Energy in Mev +# Energy bin bottom extracted from NuLib table +energies_bottom_Mev = np.array([0, 2, 4, 6.47649, 9.54299, 13.3401, 18.0418, 23.8636, 31.0725, 39.9989, 51.0519, 64.7382, 81.6853, 102.67, 128.654, 160.828, 200.668, 250]) +# Energy bin top extracted from NuLib table +energies_top_Mev = np.array([2, 4, 6.47649, 9.54299, 13.3401, 18.0418, 23.8636, 31.0725, 39.9989, 51.0519, 64.7382, 81.6853, 102.67, 128.654, 160.828, 200.668, 250, 311.085]) + +# Energies in ergs +energies_center_erg = np.array(energies_center_Mev) * 1e6 * eV # Energy in ergs +energies_bottom_erg = np.array(energies_bottom_Mev) * 1e6 * eV # Energy in ergs +energies_top_erg = np.array(energies_top_Mev ) * 1e6 * eV # Energy in ergs + +# Simulation parameters +Temperature_Mev = 5.02464 # Background matter temperature ( Mev ) +u_ee_MeV = -1.640496 # Electron neutrino chemical potential ( Mev ) +u_eebar_MeV = +1.640496 # Electron anti-neutrino chemical potential ( Mev ) +u_uu_MeV = 0.0 # Muon neutrino chemical potential ( Mev ) +u_uubar_MeV = 0.0 # Muon anti-neutrino chemical potential ( Mev ) +u_tt_MeV = 0.0 # Tauon neutrino chemical potential ( Mev ) +u_ttbar_MeV = 0.0 # Tauon anti-neutrino chemical potential ( Mev ) + +# Fermi-dirac distribution factor for electron neutrinos +f_eq_ee = 1 / ( 1 + np.exp( ( energies_center_Mev - u_ee_MeV ) / Temperature_Mev ) ) # adimentional +f_eq_eebar = 1 / ( 1 + np.exp( ( energies_center_Mev - u_eebar_MeV ) / Temperature_Mev ) ) # adimentional +f_eq_uu = 1 / ( 1 + np.exp( ( energies_center_Mev - u_uu_MeV ) / Temperature_Mev ) ) # adimentional +f_eq_uubar = 1 / ( 1 + np.exp( ( energies_center_Mev - u_uubar_MeV ) / Temperature_Mev ) ) # adimentional +f_eq_tt = 1 / ( 1 + np.exp( ( energies_center_Mev - u_tt_MeV ) / Temperature_Mev ) ) # adimentional +f_eq_ttbar = 1 / ( 1 + np.exp( ( energies_center_Mev - u_ttbar_MeV ) / Temperature_Mev ) ) # adimentional + +fee_last = np.zeros(len(energies_center_erg)) +fuu_last = np.zeros(len(energies_center_erg)) +ftt_last = np.zeros(len(energies_center_erg)) +feebar_last = np.zeros(len(energies_center_erg)) +fuubar_last = np.zeros(len(energies_center_erg)) +fttbar_last = np.zeros(len(energies_center_erg)) + +# Reading last plt* file generated +with h5py.File(directories[-1], 'r') as hf: + + N00_Re = np.array(hf['N00_Re']) # number of particles + N11_Re = np.array(hf['N11_Re']) # number of particles + N22_Re = np.array(hf['N22_Re']) # number of particles + N00_Rebar = np.array(hf['N00_Rebar']) # number of particles + N11_Rebar = np.array(hf['N11_Rebar']) # number of particles + N22_Rebar = np.array(hf['N22_Rebar']) # number of particles + + E = np.array(hf['pupt']) # ergs + t = np.array(hf['time']) # seconds + Vphase = np.array(hf['Vphase']) # seconds + + N_ee_last = np.zeros(len(energies_center_erg)) + N_uu_last = np.zeros(len(energies_center_erg)) + N_tt_last = np.zeros(len(energies_center_erg)) + N_ee_bar_last = np.zeros(len(energies_center_erg)) + N_uu_bar_last = np.zeros(len(energies_center_erg)) + N_tt_bar_last = np.zeros(len(energies_center_erg)) + Total_Vphase = np.zeros(len(energies_center_erg)) + + for i, energy_erg in enumerate(energies_center_erg): + mask = E == energy_erg + N_ee_last[i] = np.sum(N00_Re[mask]) + N_uu_last[i] = np.sum(N11_Re[mask]) + N_tt_last[i] = np.sum(N22_Re[mask]) + N_ee_bar_last[i] = np.sum(N00_Rebar[mask]) + N_uu_bar_last[i] = np.sum(N11_Rebar[mask]) + N_tt_bar_last[i] = np.sum(N22_Rebar[mask]) + Total_Vphase[i] = np.sum(Vphase[mask]) + + fee_last = ( h * clight )**3 * N_ee_last / Total_Vphase + fuu_last = ( h * clight )**3 * N_uu_last / Total_Vphase + ftt_last = ( h * clight )**3 * N_tt_last / Total_Vphase + feebar_last = ( h * clight )**3 * N_ee_bar_last / Total_Vphase + fuubar_last = ( h * clight )**3 * N_uu_bar_last / Total_Vphase + fttbar_last = ( h * clight )**3 * N_tt_bar_last / Total_Vphase + +error_fee = np.abs( ( fee_last - f_eq_ee ) / f_eq_ee ) +error_fuu = np.abs( ( fuu_last - f_eq_uu ) / f_eq_uu ) +error_ftt = np.abs( ( ftt_last - f_eq_tt ) / f_eq_tt ) +error_feebar = np.abs( ( feebar_last - f_eq_eebar ) / f_eq_eebar ) +error_fuubar = np.abs( ( fuubar_last - f_eq_uubar ) / f_eq_uubar ) +error_fttbar = np.abs( ( fttbar_last - f_eq_ttbar ) / f_eq_ttbar ) + +max_error_fee = np.max(error_fee) +max_error_fuu = np.max(error_fuu) +max_error_ftt = np.max(error_ftt) +max_error_feebar = np.max(error_feebar) +max_error_fuubar = np.max(error_fuubar) +max_error_fttbar = np.max(error_fttbar) + +print(f'max_error_fee = {max_error_fee}') +print(f'max_error_fuu = {max_error_fuu}') +print(f'max_error_ftt = {max_error_ftt}') +print(f'max_error_feebar = {max_error_feebar}') +print(f'max_error_fuubar = {max_error_fuubar}') +print(f'max_error_fttbar = {max_error_fttbar}') + +assert(np.all(max_error_fee<0.01)) +assert(np.all(max_error_fuu<0.01)) +assert(np.all(max_error_ftt<0.01)) +assert(np.all(max_error_feebar<0.01)) +assert(np.all(max_error_fuubar<0.01)) +assert(np.all(max_error_fttbar<0.01)) + +############################################################ +############################################################ +# PLOT SETTINGS +import matplotlib as mpl +from matplotlib.ticker import AutoLocator, AutoMinorLocator, LogLocator + +# Font settings +mpl.rcParams['font.size'] = 22 +mpl.rcParams['font.family'] = 'serif' +# mpl.rc('text', usetex=True) + +# Tick settings +mpl.rcParams['xtick.major.size'] = 7 +mpl.rcParams['xtick.major.width'] = 2 +mpl.rcParams['xtick.major.pad'] = 8 +mpl.rcParams['xtick.minor.size'] = 4 +mpl.rcParams['xtick.minor.width'] = 2 +mpl.rcParams['ytick.major.size'] = 7 +mpl.rcParams['ytick.major.width'] = 2 +mpl.rcParams['ytick.minor.size'] = 4 +mpl.rcParams['ytick.minor.width'] = 2 + +# Axis linewidth +mpl.rcParams['axes.linewidth'] = 2 + +# Tick direction and enabling ticks on all sides +mpl.rcParams['xtick.direction'] = 'in' +mpl.rcParams['ytick.direction'] = 'in' +mpl.rcParams['xtick.top'] = True +mpl.rcParams['ytick.right'] = True + +# Function to apply custom tick locators and other settings to an Axes object +def apply_custom_settings(ax, leg, log_scale_y=False): + + if log_scale_y: + # Use LogLocator for the y-axis if it's in log scale + ax.set_yscale('log') + ax.yaxis.set_major_locator(LogLocator(base=10.0)) + ax.yaxis.set_minor_locator(LogLocator(base=10.0, subs='auto', numticks=100)) + else: + # Use AutoLocator for regular scales + ax.yaxis.set_major_locator(AutoLocator()) + ax.yaxis.set_minor_locator(AutoMinorLocator()) + + # Apply the AutoLocator for the x-axis + ax.xaxis.set_major_locator(AutoLocator()) + ax.xaxis.set_minor_locator(AutoMinorLocator()) + + # Legend settings + leg.get_frame().set_edgecolor('w') + leg.get_frame().set_linewidth(0.0) +############################################################ +############################################################ + +Volume = 2.0e4**3 # ccm +Tot_Vphase = Volume * ( 4.0 * np.pi ) * ( ( energies_top_erg ** 3 - energies_bottom_erg ** 3 ) / 3.0 ) + +N_eq_ee = ( 1.0 / ( h * clight )**3 ) * Tot_Vphase * f_eq_ee +N_eq_uu = ( 1.0 / ( h * clight )**3 ) * Tot_Vphase * f_eq_uu +N_eq_eebar = ( 1.0 / ( h * clight )**3 ) * Tot_Vphase * f_eq_eebar +N_eq_uubar = ( 1.0 / ( h * clight )**3 ) * Tot_Vphase * f_eq_uubar + +figfee, axfee = plt.subplots() +figfeebar, axfeebar = plt.subplots() +figfuu, axfuu = plt.subplots() +figfuubar, axfuubar = plt.subplots() + +figNee, axNee = plt.subplots() +figNeebar, axNeebar = plt.subplots() +figNuu, axNuu = plt.subplots() +figNuubar, axNuubar = plt.subplots() + +# Looping over all directories +for i in range(len(directories)): + with h5py.File(directories[i], 'r') as hf: + + N00_Re = np.array(hf['N00_Re']) # number of particles + N11_Re = np.array(hf['N11_Re']) # number of particles + N00_Rebar = np.array(hf['N00_Rebar']) # number of particles + N11_Rebar = np.array(hf['N11_Rebar']) # number of particles + E = np.array(hf['pupt']) # ergs + t = np.array(hf['time']) # seconds + Vphase = np.array(hf['Vphase']) # seconds + + Nee = np.zeros(len(energies_center_erg)) + Neebar = np.zeros(len(energies_center_erg)) + Nuu = np.zeros(len(energies_center_erg)) + Nuubar = np.zeros(len(energies_center_erg)) + Total_Vphase = np.zeros(len(energies_center_erg)) + + for j, energy_erg in enumerate(energies_center_erg): + mask = E == energy_erg + Nee[j] = np.sum(N00_Re[mask]) + Neebar[j] = np.sum(N00_Rebar[mask]) + Nuu[j] = np.sum(N11_Re[mask]) + Nuubar[j] = np.sum(N11_Rebar[mask]) + Total_Vphase[j] = np.sum(Vphase[mask]) + + fee = ( h * clight )**3 * Nee / Total_Vphase + feebar = ( h * clight )**3 * Neebar / Total_Vphase + fuu = ( h * clight )**3 * Nuu / Total_Vphase + fuubar = ( h * clight )**3 * Nuubar / Total_Vphase + + # Plot the data + axfee.plot(energies_center_erg, fee, label=f't = {t[0]:.1e} s') + axfeebar.plot(energies_center_erg, feebar, label=f't = {t[0]:.1e} s') + axfuu.plot(energies_center_erg, fuu, label=f't = {t[0]:.1e} s') + axfuubar.plot(energies_center_erg, fuubar, label=f't = {t[0]:.1e} s') + + axNee.plot(energies_center_erg, Nee, label=f't = {t[0]:.1e} s') + axNeebar.plot(energies_center_erg, Neebar, label=f't = {t[0]:.1e} s') + axNuu.plot(energies_center_erg, Nuu, label=f't = {t[0]:.1e} s') + axNuubar.plot(energies_center_erg, Nuubar, label=f't = {t[0]:.1e} s') + +axfee.plot(energies_center_erg, f_eq_ee, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axfeebar.plot(energies_center_erg, f_eq_eebar, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axfuu.plot(energies_center_erg, f_eq_uu, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axfuubar.plot(energies_center_erg, f_eq_uubar, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') + +axNee.plot(energies_center_erg, N_eq_ee, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axNeebar.plot(energies_center_erg, N_eq_eebar, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axNuu.plot(energies_center_erg, N_eq_uu, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') +axNuubar.plot(energies_center_erg, N_eq_uubar, label='Fermi-Dirac T=5.02 MeV',linestyle='dotted',color = 'black') + +# Add title and labels +axfee.set_xlabel(r'E (erg)') +axfeebar.set_xlabel(r'E (erg)') +axfuu.set_xlabel(r'E (erg)') +axfuubar.set_xlabel(r'E (erg)') +axNee.set_xlabel(r'E (erg)') +axNeebar.set_xlabel(r'E (erg)') +axNuu.set_xlabel(r'E (erg)') +axNuubar.set_xlabel(r'E (erg)') + +axfee.set_ylabel(r'f_{{e}}^{{eq}}') +axfeebar.set_ylabel(r'\bar{f}_{{e}}^{{eq}}') +axfuu.set_ylabel(r'f_{{u}}^{{eq}}') +axfuubar.set_ylabel(r'\bar{f}_{{u}}^{{eq}}') + +axNee.set_ylabel(r'N_{{e}}') +axNeebar.set_ylabel(r'\bar{N}_{{e}}') +axNuu.set_ylabel(r'N_{{u}}') +axNuubar.set_ylabel(r'\bar{N}_{{u}}') + +# Add a legend +legfee = axfee.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axfee, legfee) +legfeebar = axfeebar.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axfeebar, legfeebar) +legfuu = axfuu.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axfuu, legfuu) +legfuubar = axfuubar.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axfuubar, legfuubar) + +legNee = axNee.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axNee, legNee) +legNeebar = axNeebar.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axNeebar, legNeebar) +legNuu = axNuu.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axNuu, legNuu) +legNuubar = axNuubar.legend(framealpha=0.0, ncol=1, fontsize=14) +apply_custom_settings(axNuubar, legNuubar) + +# Save the plot as a PDF file +figfee.savefig('figfee.pdf',bbox_inches='tight') +figfeebar.savefig('figfeebar.pdf',bbox_inches='tight') +figfuu.savefig('figfuu.pdf',bbox_inches='tight') +figfuubar.savefig('figfuubar.pdf',bbox_inches='tight') +figNee.savefig('figNee.pdf',bbox_inches='tight') +figNeebar.savefig('figNeebar.pdf',bbox_inches='tight') +figNuu.savefig('figNuu.pdf',bbox_inches='tight') +figNuubar.savefig('figNuubar.pdf',bbox_inches='tight') + +plt.clf() \ No newline at end of file diff --git a/Scripts/tests/msw_test.py b/Scripts/tests/msw_test.py index ce90c898..aab770cb 100644 --- a/Scripts/tests/msw_test.py +++ b/Scripts/tests/msw_test.py @@ -4,7 +4,7 @@ import EmuReader import sys import os -importpath = os.path.dirname(os.path.realpath(__file__))+"/../visualization/" +importpath = os.path.dirname(os.path.realpath(__file__))+"/../data_reduction/" sys.path.append(importpath) import amrex_plot_tools as amrex @@ -20,6 +20,7 @@ dm21c4 = 7.39e-5 * eV**2 # erg^2 mp = 1.6726219e-24 # g GF = 1.1663787e-5 / (1e9*eV)**2 * (hbar*clight)**3 #erg cm^3 +NF = 2 tolerance = 7e-2 @@ -31,13 +32,13 @@ if __name__ == "__main__": - rkey, ikey = amrex.get_particle_keys() + rkey, ikey = amrex.get_particle_keys(NF) t = [] - fee = [] - fxx = [] - feebar = [] - fxxbar = [] + Nee = [] + Nxx = [] + Neebar = [] + Nxxbar = [] pupt = [] nfiles = len(glob.glob("plt[0-9][0-9][0-9][0-9][0-9]")) @@ -47,17 +48,17 @@ idata, rdata = EmuReader.read_particle_data(plotfile, ptype="neutrinos") p = rdata[0] t.append(p[rkey["time"]]) - fee.append(p[rkey["f00_Re"]]) - fxx.append(p[rkey["f11_Re"]]) - feebar.append(p[rkey["f00_Rebar"]]) - fxxbar.append(p[rkey["f11_Rebar"]]) + Nee.append(p[rkey["N00_Re"]]) + Nxx.append(p[rkey["N11_Re"]]) + Neebar.append(p[rkey["N00_Rebar"]]) + Nxxbar.append(p[rkey["N11_Rebar"]]) pupt.append(p[rkey["pupt"]]) t = np.array(t) - fee = np.array(fee) - fxx = np.array(fxx) - feebar = np.array(feebar) - fxxbar = np.array(fxxbar) + Nee = np.array(Nee) + Nxx = np.array(Nxx) + Neebar = np.array(Neebar) + Nxxbar = np.array(Nxxbar) # The neutrino energy we set #E = dm21c4 * np.sin(2.*theta12) / (8.*np.pi*hbar*clight) @@ -82,31 +83,31 @@ def myassert(condition): assert(condition) # calculate errors - fee_analytic = Psurv(dm2_eff, sin2_eff, E) - error_ee = np.max(np.abs( fee - fee_analytic ) ) + Nee_analytic = Psurv(dm2_eff, sin2_eff, E) + error_ee = np.max(np.abs( Nee - Nee_analytic ) ) print("f_ee error:", error_ee) myassert( error_ee < tolerance ) - fxx_analytic = 1. - Psurv(dm2_eff, sin2_eff, E) - error_xx = np.max(np.abs( fxx - fxx_analytic ) ) + Nxx_analytic = 1. - Psurv(dm2_eff, sin2_eff, E) + error_xx = np.max(np.abs( Nxx - Nxx_analytic ) ) print("f_xx error:", error_xx) myassert( error_xx < tolerance ) - feebar_analytic = Psurv(dm2_effbar, sin2_effbar, E) - error_eebar = np.max(np.abs( feebar - feebar_analytic ) ) + Neebar_analytic = Psurv(dm2_effbar, sin2_effbar, E) + error_eebar = np.max(np.abs( Neebar - Neebar_analytic ) ) print("f_eebar error:", error_eebar) myassert( error_eebar < tolerance ) - fxxbar_analytic = 1. - Psurv(dm2_effbar, sin2_effbar, E) - error_xxbar = np.max(np.abs( fxxbar - fxxbar_analytic ) ) + Nxxbar_analytic = 1. - Psurv(dm2_effbar, sin2_effbar, E) + error_xxbar = np.max(np.abs( Nxxbar - Nxxbar_analytic ) ) print("f_xxbar error:", error_xxbar) myassert( error_xxbar < tolerance ) - conservation_error = np.max(np.abs( (fee+fxx) -1. )) + conservation_error = np.max(np.abs( (Nee+Nxx) -1. )) print("conservation_error:", conservation_error) myassert(conservation_error < tolerance) - conservation_errorbar = np.max(np.abs( (feebar+fxxbar) -1. )) + conservation_errorbar = np.max(np.abs( (Neebar+Nxxbar) -1. )) print("conservation_errorbar:", conservation_errorbar) myassert(conservation_errorbar < tolerance) diff --git a/Scripts/tests/plot_convergence.py b/Scripts/tests/plot_convergence.py deleted file mode 100644 index 4be2b8c3..00000000 --- a/Scripts/tests/plot_convergence.py +++ /dev/null @@ -1,98 +0,0 @@ -import numpy as np -import matplotlib.pyplot as plt - -# Read forward Euler data -class ConvergenceData(object): - def __init__(self, filename=None): - self.data = {"cfl": [], - "f_ee error": [], - "f_xx error": [], - "f_eebar error": [], - "f_xxbar error": []} - - if filename: - self.readfrom(filename) - - def readfrom(self, filename): - f = open(filename, "r") - - while True: - entry = [f.readline().strip() for i in range(9)] - if not entry[0]: - break - for line in entry: - ls = line.split(":") - name = ls[0].strip() - value = ls[-1].strip() - for k in self.data.keys(): - if name == k: - self.data[k].append(float(value)) - - f.close() - - for k in self.data.keys(): - self.data[k] = np.array(self.data[k]) - - def get(self, key): - return self.data[key] - - def keys(self): - return self.data.keys() - - def error_keys(self): - return [k for k in self.data.keys() if k != "cfl"] - - def average_convergence(self, key): - # get the average convergence order for the keyed quantity - err = self.get(key) - cfl = self.get("cfl") - - orders = [] - for i in range(len(err)-1): - order = np.log10(err[i+1]/err[i]) / np.log10(cfl[i+1]/cfl[i]) - orders.append(order) - orders = np.array(orders) - - order_average = np.average(orders) - return order_average - - def plot_on_axis(self, axis, key, label, color): - log_cfl = np.log10(self.get("cfl")) - log_err = np.log10(self.get(key)) - axis.plot(log_cfl, log_err, label=label, marker="o", linestyle="None", color=color) - - order = self.average_convergence(key) - iMaxErr = np.argmax(log_err) - intercept = log_err[iMaxErr] - order * log_cfl[iMaxErr] - log_order_err = intercept + order * log_cfl - axis.plot(log_cfl, log_order_err, label="$O({}) = {:0.2f}$".format(label, order), marker="None", linestyle="--", color=color) - - -cdata = {} -cdata["fe"] = ConvergenceData("msw_test_fe.txt") -cdata["trapz"] = ConvergenceData("msw_test_trapz.txt") -cdata["ssprk3"] = ConvergenceData("msw_test_ssprk3.txt") -cdata["rk4"] = ConvergenceData("msw_test_rk4.txt") - -variables = cdata["fe"].error_keys() - -for v in variables: - fig, ax = plt.subplots() - - ax.set_xlabel("log10 flavor CFL") - ax.set_ylabel("log10 {}".format(v)) - - colors = ["red", "blue", "green", "magenta"] - - for k, c in zip(cdata.keys(), colors): - cd = cdata[k] - cd.plot_on_axis(ax, v, k, c) - - ax.invert_xaxis() - - ax.legend(loc=(1.05, 0.0)) - fig.tight_layout() - - plt.savefig("convergence_{}.eps".format(v.replace(" ","_"))) - plt.savefig("convergence_{}.png".format(v.replace(" ","_")), dpi=300) - plt.clf() diff --git a/Scripts/visualization/plot_all_particles.py b/Scripts/visualization/plot_all_particles.py index b2f6f97b..67f45b20 100644 --- a/Scripts/visualization/plot_all_particles.py +++ b/Scripts/visualization/plot_all_particles.py @@ -1,12 +1,17 @@ import numpy as np import argparse import glob +import os +import sys +importpath = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(importpath) +sys.path.append(importpath+"/../data_reduction") import amrex_plot_tools as amrex if __name__ == "__main__": import pylab as plt - rkey, ikey = amrex.get_particle_keys() + rkey, ikey = amrex.get_particle_keys(2) t = [] fee = [] diff --git a/Scripts/visualization/plot_grid.py b/Scripts/visualization/plot_grid.py index 70d23e01..e0f1fd6b 100644 --- a/Scripts/visualization/plot_grid.py +++ b/Scripts/visualization/plot_grid.py @@ -10,6 +10,7 @@ def make_plot(d): plt.clf() #plt.ylim(-1.5e40,1.5e40) ds = yt.load(d) + t = ds.current_time ad = ds.all_data() Re = ad['boxlib',base+"_Re"] Im = ad['boxlib',base+"_Im"] @@ -22,6 +23,7 @@ def make_plot(d): plt.plot(Re,color="blue",linestyle="--") plt.plot(Im,color="orange",linestyle="--") # plt.plot(mag) + plt.text(0,0,"t="+str(t)+" s") plt.savefig(base+"_"+d+".png") directories = sorted(glob.glob("plt*")) @@ -31,4 +33,4 @@ def make_plot(d): for d in directories: make_plot(d) -# ffmpeg -i N01_%05d.png -pix_fmt yuv420p movie.mp4 +# ffmpeg -i %*.png -pix_fmt yuv420p movie.mp4 diff --git a/Source/ArithmeticArray.H b/Source/ArithmeticArray.H new file mode 100644 index 00000000..36d32403 --- /dev/null +++ b/Source/ArithmeticArray.H @@ -0,0 +1,93 @@ +template +class ArithmeticArray{ +public: + std::array data; + + AMREX_GPU_HOST_DEVICE + ArithmeticArray(){} + + AMREX_GPU_HOST_DEVICE + ArithmeticArray(T in){ + for(size_t i=0; i + AMREX_GPU_HOST_DEVICE + ArithmeticArray(ArithmeticArray in){ + for(size_t i=0; i + AMREX_GPU_HOST_DEVICE + ArithmeticArray& operator=(const ArithmeticArray& input){ + for(size_t i=0; i& operator=(const double input){ + for(size_t i=0; i operator*(const double scale) const{ + ArithmeticArray result; + for(size_t i=0; i operator/(const double scale) const{ + double inv_scale = 1./scale; + return operator*(inv_scale); + } + template + AMREX_GPU_HOST_DEVICE + const ArithmeticArray operator+(const ArithmeticArray& input) const{ + ArithmeticArray result; + for(size_t i=0; i + AMREX_GPU_HOST_DEVICE + const ArithmeticArray operator-(const ArithmeticArray& input) const{ + ArithmeticArray result; + for(size_t i=0; i operator-() const{ + ArithmeticArray result; + for(size_t i=0; i + AMREX_GPU_HOST_DEVICE + void operator+=(const ArithmeticArray& input){ + for(size_t i=0; i + AMREX_GPU_HOST_DEVICE + bool operator==(const ArithmeticArray& input){ + bool isequal = true; + for(size_t i=0; i +#include +#include +#include +#include +#include + +//We use the AMReX binary format to write data for now +//That's because the HDF5 write format gives errors when running with CUDA. +#undef AMREX_USE_HDF5 + +#ifdef AMREX_USE_HDF5 +#include <../submodules/HighFive/include/highfive/H5File.hpp> +#include <../submodules/HighFive/include/highfive/H5DataSpace.hpp> +#include <../submodules/HighFive/include/highfive/H5DataSet.hpp> +#endif + +class DataReducer{ +private: + +#ifdef AMREX_USE_HDF5 + const char* filename0D = "reduced0D.h5"; + const HighFive::DataSpace dataspace = HighFive::DataSpace({0}, {HighFive::DataSpace::UNLIMITED}); +#else + const char* filename0D = "reduced0D.dat"; +#endif + +public: + void + InitializeFiles(); + + void + WriteReducedData0D(const amrex::Geometry& geom, + const MultiFab& state, + const FlavoredNeutrinoContainer& neutrinos, + amrex::Real time, int step); +}; + +#endif diff --git a/Source/DataReducer.cpp b/Source/DataReducer.cpp new file mode 100644 index 00000000..b90799e1 --- /dev/null +++ b/Source/DataReducer.cpp @@ -0,0 +1,346 @@ +#include "Evolve.H" +#include "Constants.H" +#include "DataReducer.H" +#include "ArithmeticArray.H" +#include +#include + +//We use the AMReX binary format to write data for now +//That's because the HDF5 write format gives errors when running with CUDA. +#undef AMREX_USE_HDF5 + +#ifdef AMREX_USE_HDF5 +#include <../submodules/HighFive/include/highfive/H5File.hpp> +#include <../submodules/HighFive/include/highfive/H5DataSpace.hpp> +#include <../submodules/HighFive/include/highfive/H5DataSet.hpp> +#endif + +#ifdef AMREX_USE_HDF5 +// append a single scalar to the specified file and dataset +template +void append_0D(HighFive::File& file0D, const std::string& datasetname, const T value){ + HighFive::DataSet dataset_step = file0D.getDataSet(datasetname); + std::vector dims = dataset_step.getDimensions(); + dims[0] ++; + dataset_step.resize(dims); + dataset_step.select({dims[0]-1},{1}).write((T[1]){value}); +} +#endif + +void DataReducer::InitializeFiles() +{ + if (ParallelDescriptor::IOProcessor()) + { + +#ifdef AMREX_USE_HDF5 + + using namespace HighFive; + File file0D(filename0D, File::Truncate | File::Create); + + DataSetCreateProps props; + props.add(Chunking(std::vector{1})); + file0D.createDataSet("step", dataspace, create_datatype(), props); + file0D.createDataSet("time(s)", dataspace, create_datatype(), props); + file0D.createDataSet("Ntot(1|ccm)", dataspace, create_datatype(), props); + file0D.createDataSet("Ndiff(1|ccm)", dataspace, create_datatype(), props); + for (int i = 0; i < NUM_FLAVORS; i++) + { + file0D.createDataSet(std::string("N") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("N") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fx") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fy") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fz") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fx") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fy") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Fz") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + #if NUM_MOMENTS == 3 + file0D.createDataSet(std::string("Pxx") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pxy") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pxz") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pyy") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pyz") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pzz") + std::to_string(i) + std::to_string(i) + std::string("(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pxx") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pxy") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pxz") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pyy") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pyz") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + file0D.createDataSet(std::string("Pzz") + std::to_string(i) + std::to_string(i) + std::string("bar(1|ccm)"), dataspace, create_datatype(), props); + #endif + } + file0D.createDataSet("N_offdiag_mag(1|ccm)", dataspace, create_datatype(), props); + file0D.createDataSet("sumTrN", dataspace, create_datatype(), props); + file0D.createDataSet("sumTrHN", dataspace, create_datatype(), props); + +#else + + std::ofstream outfile; + outfile.open(filename0D, std::ofstream::out); + int j = 0; + j++; outfile << j << ":step\t"; + j++; outfile << j << ":time(s)\t"; + j++; outfile << j << ":Ntot(1|ccm)\t"; + j++; outfile << j << ":Ndiff(1|ccm)\t"; + for (int i = 0; i < NUM_FLAVORS; i++) + { + j++; outfile << j << ":N" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":N" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Fx" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Fy" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Fz" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Fx" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Fy" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Fz" << i << i << "bar(1|ccm)\t"; + #if NUM_MOMENTS == 3 + j++; outfile << j << ":Pxx" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pxy" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pxz" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pyy" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pyz" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pzz" << i << i << "(1|ccm)\t"; + j++; outfile << j << ":Pxx" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Pxy" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Pxz" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Pyy" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Pyz" << i << i << "bar(1|ccm)\t"; + j++; outfile << j << ":Pzz" << i << i << "bar(1|ccm)\t"; + #endif + } + j++; + outfile << j << ":N_offdiag_mag(1|ccm)\t"; + j++; + outfile << j << ":sumTrN\t"; + j++; + outfile << j << ":sumTrHN\t"; + j++; + outfile << j << ":Vphase\t"; + outfile << std::endl; + outfile.close(); + +#endif + } +} + +void +DataReducer::WriteReducedData0D(const amrex::Geometry& geom, + const MultiFab& state, + const FlavoredNeutrinoContainer& neutrinos, + const amrex::Real time, const int step) +{ + // get index volume of the domain + int ncells = geom.Domain().volume(); + + //==================================// + // Do reductions over the particles // + //==================================// + using PType = typename FlavoredNeutrinoContainer::ParticleType; + amrex::ReduceOps reduce_ops; + auto particleResult = amrex::ParticleReduce< ReduceData >(neutrinos, + [=] AMREX_GPU_DEVICE(const PType& p) noexcept -> amrex::GpuTuple { + Real TrHN = p.rdata(PIdx::TrHN); + Real TrN = 0; + Real Vphase = p.rdata(PIdx::Vphase); +#include "generated_files/DataReducer.cpp_fill_particles" + return GpuTuple{TrN,TrHN, Vphase}; + }, reduce_ops); + Real TrN = amrex::get<0>(particleResult); + Real TrHN = amrex::get<1>(particleResult); + Real Vphase = amrex::get<2>(particleResult); + ParallelDescriptor::ReduceRealSum(TrN); + ParallelDescriptor::ReduceRealSum(TrHN); + ParallelDescriptor::ReduceRealSum(Vphase); + + printf("TrN=%g, TrHN=%g, Vphase=%g\n", TrN, TrHN, Vphase); + + //=============================// + // Do reductions over the grid // + //=============================// + // first, get a reference to the data arrays + auto const& ma = state.const_arrays(); + IntVect nghost(AMREX_D_DECL(0, 0, 0)); + + // use the ParReduce function to define reduction operator + #if NUM_MOMENTS == 2 + GpuTuple< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray > result = + ParReduce(TypeList{}, + TypeList< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray >{}, + state, nghost, + [=] AMREX_GPU_DEVICE(int box_no, int i, int j, int k) noexcept -> + GpuTuple< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray > { + #elif NUM_MOMENTS == 3 + GpuTuple< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray > result = + ParReduce(TypeList{}, + TypeList< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray >{}, + state, nghost, + [=] AMREX_GPU_DEVICE(int box_no, int i, int j, int k) noexcept -> + GpuTuple< ArithmeticArray, ArithmeticArray, Real , ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray, ArithmeticArray > { + #else + #error "NUM_MOMENTS must be 2 or 3" + #endif + Array4 const& a = ma[box_no]; + + // Doing the actual work + ArithmeticArray Ndiag, Ndiagbar; + ArithmeticArray Fxdiag, Fxdiagbar; + ArithmeticArray Fydiag, Fydiagbar; + ArithmeticArray Fzdiag, Fzdiagbar; + #if NUM_MOMENTS >= 2 + ArithmeticArray Pxxdiag, Pxxdiagbar; + ArithmeticArray Pxydiag, Pxydiagbar; + ArithmeticArray Pxzdiag, Pxzdiagbar; + ArithmeticArray Pyydiag, Pyydiagbar; + ArithmeticArray Pyzdiag, Pyzdiagbar; + ArithmeticArray Pzzdiag, Pzzdiagbar; + #endif + + Real N_offdiag_mag2 = 0; + + #include "generated_files/DataReducer.cpp_fill" + #if NUM_MOMENTS == 2 + return {Ndiag, Ndiagbar, N_offdiag_mag2, Fxdiag, Fydiag, Fzdiag, Fxdiagbar,Fydiagbar,Fzdiagbar}; + #elif NUM_MOMENTS == 3 + return {Ndiag, Ndiagbar, N_offdiag_mag2, Fxdiag, Fydiag, Fzdiag, Fxdiagbar,Fydiagbar,Fzdiagbar, Pxxdiag, Pxydiag, Pxzdiag, Pyydiag, Pyzdiag, Pzzdiag, Pxxdiagbar, Pxydiagbar, Pxzdiagbar, Pyydiagbar, Pyzdiagbar, Pzzdiagbar}; + #else + #error "NUM_MOMENTS must be 2 or 3" + #endif + }); + + // retrieve the reduced data values + ArithmeticArray N = amrex::get<0>(result) / ncells; + ArithmeticArray Nbar = amrex::get<1>(result) / ncells; + Real N_offdiag_mag2 = amrex::get<2>(result) / ncells; + ArithmeticArray Fx = amrex::get<3>(result) / ncells; + ArithmeticArray Fy = amrex::get<4>(result) / ncells; + ArithmeticArray Fz = amrex::get<5>(result) / ncells; + ArithmeticArray Fxbar = amrex::get<6>(result) / ncells; + ArithmeticArray Fybar = amrex::get<7>(result) / ncells; + ArithmeticArray Fzbar = amrex::get<8>(result) / ncells; + #if NUM_MOMENTS == 3 + ArithmeticArray Pxx = amrex::get< 9>(result) / ncells; + ArithmeticArray Pxy = amrex::get<10>(result) / ncells; + ArithmeticArray Pxz = amrex::get<11>(result) / ncells; + ArithmeticArray Pyy = amrex::get<12>(result) / ncells; + ArithmeticArray Pyz = amrex::get<13>(result) / ncells; + ArithmeticArray Pzz = amrex::get<14>(result) / ncells; + ArithmeticArray Pxxbar= amrex::get<15>(result) / ncells; + ArithmeticArray Pxybar= amrex::get<16>(result) / ncells; + ArithmeticArray Pxzbar= amrex::get<17>(result) / ncells; + ArithmeticArray Pyybar= amrex::get<18>(result) / ncells; + ArithmeticArray Pyzbar= amrex::get<19>(result) / ncells; + ArithmeticArray Pzzbar= amrex::get<20>(result) / ncells; + #endif + + // further reduce over mpi ranks + for(int i=0; i (y)) ? (x) : (y)) +#define MIN2(x, y) (((x) < (y)) ? (x) : (y)) +#define NTABLES 19 +#define LENGTHGF 6.77269222552442e-06 +#define TIMEGF 2.03040204956746e05 +#define RHOGF 1.61887093132742e-18 +#define PRESSGF 1.80123683248503e-39 +#define EPSGF 1.11265005605362e-21 +#define INVRHOGF 6.17714470405638e17 +#define INVEPSGF 8.98755178736818e20 +#define INVPRESSGF 5.55174079257738e38 +#define CLIGHT 1 + +// table key +// 0 logpress +// 1 logenergy +// 2 entropy +// 3 munu +// 4 cs2 +// 5 dedt +// 6 dpdrhoe +// 7 dpderho +// 8 muhat +// 9 mu_e +// 10 mu_p +// 11 mu_n +// 12 Xa +// 13 Xh +// 14 Xn +// 15 Xp +// 16 Abar +// 17 Zbar +// 18 Gamma +// enum eos_var {i_logpress=0, i_logenergy, i_entropy, i_munu, i_cs2, i_dedt, + //i_dpdrhoe, i_dpderho, i_muhat, i_mu_e, i_mu_p, i_mu_n, i_Xa, + //i_Xh, i_Xn, i_Xp, i_Abar, i_Zbar, i_Gamma}; +//} + +namespace nuc_eos_private { + +// table data + extern double *alltables; + extern double *epstable; + extern double *logrho; + extern double *logtemp; + extern double *yes; + +#define EOSVAR(VAR) helperVarsReal[helperVarsEnumReal::VAR] +//WARNING: If the number of variables is increased here, then memory allocation for helperVarsReal/helperVarsInt pointer in GRHydroX_ReadEOSTable.cxx will also need to be increased. + enum helperVarsEnumReal { eos_rhomin, eos_rhomax, eos_tempmin, eos_tempmax, eos_yemin, eos_yemax, + energy_shift, dlintemp, dlintempi, drholintempi, dlintempyei, drholintempyei, + dtemp, dtempi, drho, drhoi, dye, dyei, drhotempi, drhoyei, dtempyei, + drhotempyei, eos_epsmin, eos_epsmax }; + extern double *helperVarsReal; + +#define EOSVAR_INT(VAR) helperVarsInt[helperVarsEnumInt::VAR] + enum helperVarsEnumInt { nrho, ntemp, nye } ; + extern int *helperVarsInt; + + +} + +#endif // EOS_TABLE_H diff --git a/Source/EosTableFunctions.H b/Source/EosTableFunctions.H new file mode 100644 index 00000000..fdd6aba9 --- /dev/null +++ b/Source/EosTableFunctions.H @@ -0,0 +1,42 @@ +#ifndef EOSTABLEFUCNTIONS_HXX +#define EOSTABLEFUCNTIONS_HXX + + +#include "EosTableHelpers.H" + +struct EOS_tabulated { + double *alltables, *epstable, *logrho, *logtemp, *yes, *helperVarsReal; + int *helperVarsInt; + + //constructor for Tabulated EOS + AMREX_GPU_DEVICE AMREX_GPU_HOST EOS_tabulated() = default;//Need to keep it + AMREX_GPU_DEVICE AMREX_GPU_HOST EOS_tabulated(double *alltables, double *epstable, double *logrho, + double *logtemp, double *yes, double *helperVarsReal, int *helperVarsInt): + alltables(alltables), epstable(epstable), logrho(logrho), logtemp(logtemp), + yes(yes), helperVarsReal(helperVarsReal), helperVarsInt(helperVarsInt) {} + + //--------------- get helperVarsReal pointer --------------------------------------------- + AMREX_GPU_DEVICE AMREX_GPU_HOST double *get_helperVarsReal() const { + return helperVarsReal; + };//get_helperVarsReal + + + //--------------- get mu_e and muhat for tabulated EOS --------------------------------------------- + AMREX_GPU_DEVICE AMREX_GPU_HOST void get_mue_muhat(double rho, double temperature, double Ye, + double &mue, double &muhat, + int &keyerr, int &anyerr) const { + + nuc_eos_mue_muhat(rho, temperature, Ye, mue, muhat, keyerr, anyerr, + alltables, logrho, logtemp, yes, helperVarsReal, helperVarsInt); + return; + + /*Actual steps: + (1) check bounds + (2) get interp spots + (3) do interpolation to get mu_e and muhat + */ + };//get_mue_muhat + +}; //struct EOS_tabulated + +#endif // EOSTABLEFUCNTIONS_HXX \ No newline at end of file diff --git a/Source/EosTableHelpers.H b/Source/EosTableHelpers.H new file mode 100644 index 00000000..70ac0637 --- /dev/null +++ b/Source/EosTableHelpers.H @@ -0,0 +1,176 @@ +#ifndef EOSTABLEHELPERS_HXX +#define EOSTABLEHELPERS_HXX + +#include "EosTable.H" + +//Check whether input (rho, temperature, Ye) are within the table bounds. +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST int +checkbounds(const double xrho, const double xtemp, const double xye, double *helperVarsReal) { + + using namespace nuc_eos_private; + // keyerr codes: + // 101 -- Y_e too high + // 102 -- Y_e too low + // 103 -- temp too high (if keytemp = 1) + // 104 -- temp too low (if keytemp = 1) + // 105 -- rho too high + // 106 -- rho too low + + if(xrho > EOSVAR(eos_rhomax)) { + return 105; + } + if(xrho < EOSVAR(eos_rhomin)) { + return 106; + } + if(xye > EOSVAR(eos_yemax)) { + return 101; + } + if(xye < EOSVAR(eos_yemin)) { + return 102; + } + if(xtemp > EOSVAR(eos_tempmax)) { + return 103; + } + if(xtemp < EOSVAR(eos_tempmin)) { + return 104; + } + return 0; + +}//function checkbounds + +//Get the indices to prepare for interpolation. +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +get_interp_spots(const double x, const double y, const double z, double &delx, + double &dely, double &delz, int *idx, double *logrho, + double *logtemp, double *yes, double *helperVarsReal, int *helperVarsInt) +{ + using namespace nuc_eos_private; + + int ix = 1 + (int)( (x - logrho[0] - 1.0e-10) * EOSVAR(drhoi) ); + int iy = 1 + (int)( (y - logtemp[0] - 1.0e-10) * EOSVAR(dtempi) ); + int iz = 1 + (int)( (z - yes[0] - 1.0e-10) * EOSVAR(dyei) ); + + const int nrho = EOSVAR_INT(nrho); + const int ntemp = EOSVAR_INT(ntemp); + const int nye = EOSVAR_INT(nye); + + ix = MAX2( 1, MIN2( ix, nrho-1 ) ); + iy = MAX2( 1, MIN2( iy, ntemp-1 ) ); + iz = MAX2( 1, MIN2( iz, nye-1 ) ); + + idx[0] = NTABLES*(ix + nrho*(iy + ntemp*iz)); + idx[1] = NTABLES*((ix-1) + nrho*(iy + ntemp*iz)); + idx[2] = NTABLES*(ix + nrho*((iy-1) + ntemp*iz)); + idx[3] = NTABLES*(ix + nrho*(iy + ntemp*(iz-1))); + idx[4] = NTABLES*((ix-1) + nrho*((iy-1) + ntemp*iz)); + idx[5] = NTABLES*((ix-1) + nrho*(iy + ntemp*(iz-1))); + idx[6] = NTABLES*(ix + nrho*((iy-1) + ntemp*(iz-1))); + idx[7] = NTABLES*((ix-1) + nrho*((iy-1) + ntemp*(iz-1))); + + // set up aux vars for interpolation + delx = logrho[ix] - x; + dely = logtemp[iy] - y; + delz = yes[iz] - z; + + return; +} // function get_interp_spots + +//Perform the interpolation +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +nuc_eos_C_linterp_one(const int *idx, const double delx, const double dely, + const double delz, double &f, const int iv, + double *alltables, double *helperVarsReal) +{ + using namespace nuc_eos_private; + + // helper variables + double fh[8], a[8]; + + fh[0] = alltables[iv+idx[0]]; + fh[1] = alltables[iv+idx[1]]; + fh[2] = alltables[iv+idx[2]]; + fh[3] = alltables[iv+idx[3]]; + fh[4] = alltables[iv+idx[4]]; + fh[5] = alltables[iv+idx[5]]; + fh[6] = alltables[iv+idx[6]]; + fh[7] = alltables[iv+idx[7]]; + + // set up coeffs of interpolation polynomical and + // evaluate function values + a[0] = fh[0]; + a[1] = EOSVAR(drhoi) * ( fh[1] - fh[0] ); + a[2] = EOSVAR(dtempi) * ( fh[2] - fh[0] ); + a[3] = EOSVAR(dyei) * ( fh[3] - fh[0] ); + a[4] = EOSVAR(drhotempi) * ( fh[4] - fh[1] - fh[2] + fh[0] ); + a[5] = EOSVAR(drhoyei) * ( fh[5] - fh[1] - fh[3] + fh[0] ); + a[6] = EOSVAR(dtempyei) * ( fh[6] - fh[2] - fh[3] + fh[0] ); + a[7] = EOSVAR(drhotempyei) * ( fh[7] - fh[0] + fh[1] + fh[2] + + fh[3] - fh[4] - fh[5] - fh[6] ); + + f = a[0] + a[1] * delx + + a[2] * dely + + a[3] * delz + + a[4] * delx * dely + + a[5] * delx * delz + + a[6] * dely * delz + + a[7] * delx * dely * delz; + + return; +} // function nuc_eos_C_linterp_one + + + +//Main function for mu_e and muhat given (rho, temperature, Ye) +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +nuc_eos_mue_muhat(const double rho, const double temperature, + const double Ye, double &mue, double &muhat, + int &keyerr, int &anyerr, double *alltables, + double *logrho, double *logtemp, double *yes, + double *helperVarsReal, int *helperVarsInt) +{ + using namespace nuc_eos_private; + + int anyerr_ = 0; + // check if we are fine + int keyerr_ = checkbounds(rho, temperature, Ye, helperVarsReal); + if(keyerr_ != 0) anyerr_ = 1; + + keyerr = keyerr_ ; anyerr = anyerr_; + // Abort if there is any error in checkbounds. + // This should never happen and the program should abort with + // a fatal error anyway. No point in doing any further EOS calculations. + if(anyerr_){ + printf("Error in checkbounds::%s::%d::%s, keyerr = %d\n", __FILE__, __LINE__, __func__, keyerr); + printf(" rho = %.5e, temperature = %.5e, Ye = %.6f \n", rho, temperature, Ye); + return; + } + + int idx[8]; + double delx,dely,delz; + const double lrho = log(rho); + const double ltemp = log(temperature); + + get_interp_spots(lrho, ltemp, Ye, delx, dely, delz, idx, + logrho, logtemp, yes, helperVarsReal, helperVarsInt); + + double mue_ , muhat_; + { + const int iv = 9; + nuc_eos_C_linterp_one(idx, delx, dely, delz, mue_, iv, alltables, helperVarsReal); + } + + { + const int iv = 8; + nuc_eos_C_linterp_one(idx, delx, dely, delz, muhat_, iv, alltables, helperVarsReal); + } + + // Assign values to reference variables: + mue = mue_; + muhat = muhat_; + + return; +}//nuc_eos_mue_muhat + + + +#endif //EOSTABLEHELPERS_HXX \ No newline at end of file diff --git a/Source/Evolve.H b/Source/Evolve.H index ed387f1a..61e5ada3 100644 --- a/Source/Evolve.H +++ b/Source/Evolve.H @@ -24,12 +24,29 @@ namespace GIdx extern amrex::Vector names; void Initialize(); -}; +} -amrex::Real compute_dt(const amrex::Geometry& geom, const amrex::Real cfl_factor, const MultiFab& state, const FlavoredNeutrinoContainer& neutrinos, const Real flavor_cfl_factor, const Real max_adaptive_speedup); +amrex::Real compute_dt(const amrex::Geometry& geom, const MultiFab& state, const FlavoredNeutrinoContainer& neutrinos, const TestParams* parms); void deposit_to_mesh(const FlavoredNeutrinoContainer& neutrinos, amrex::MultiFab& state, const amrex::Geometry& geom); void interpolate_rhs_from_mesh(FlavoredNeutrinoContainer& neutrinos_rhs, const amrex::MultiFab& state, const amrex::Geometry& geom, const TestParams* parms); +/** + * @brief Sets the N and Nbar to zero for particles inside the black hole or boundary cells. + * + * This function iterates over all particles in the `FlavoredNeutrinoContainer` and sets N and Nbar to zero if pariticles are inside the black hole or within the boundary cells of the simulation domain. + * + * @param neutrinos Reference to the container holding the flavored neutrinos. + * @param parms Pointer to the structure containing test parameters, including black hole properties and domain dimensions. + * + * The function performs the following steps: + * - Iterates over all particles in the container. + * - Computes the distance of each particle from the black hole center. + * - Sets N and Nbar to zero if the particle is inside the black hole radius. + * - Sets N and Nbar to zero if the particle is within the boundary cells of the simulation domain. + * + */ +void empty_particles_at_boundary_cells(FlavoredNeutrinoContainer& neutrinos, const TestParams* parms); + #endif diff --git a/Source/Evolve.cpp b/Source/Evolve.cpp index a1fbfe98..eb939812 100644 --- a/Source/Evolve.cpp +++ b/Source/Evolve.cpp @@ -1,8 +1,15 @@ +#include "FlavoredNeutrinoContainer.H" #include "Evolve.H" #include "Constants.H" #include "ParticleInterpolator.H" #include +#include "EosTableFunctions.H" +#include "EosTable.H" + +#include "NuLibTableFunctions.H" +#include "NuLibTable.H" + using namespace amrex; namespace GIdx @@ -19,22 +26,19 @@ namespace GIdx } } -Real compute_dt(const Geometry& geom, const Real cfl_factor, const MultiFab& state, const FlavoredNeutrinoContainer& neutrinos, const Real flavor_cfl_factor, const Real max_adaptive_speedup) +Real compute_dt(const Geometry& geom, const MultiFab& state, const FlavoredNeutrinoContainer& /* neutrinos */, const TestParams* parms) { - AMREX_ASSERT(cfl_factor > 0.0 || flavor_cfl_factor > 0.0); - - const auto dx = geom.CellSizeArray(); - const Real cell_volume = dx[0]*dx[1]*dx[2]; + AMREX_ASSERT(parms->cfl_factor > 0.0 || parms->flavor_cfl_factor > 0.0 || parms->collision_cfl_factor > 0.0); // translation part of timestep limit const auto dxi = geom.CellSizeArray(); Real dt_translation = 0.0; - if (cfl_factor > 0.0) { - dt_translation = std::min(std::min(dxi[0],dxi[1]), dxi[2]) / PhysConst::c * cfl_factor; + if (parms->cfl_factor > 0.0) { + dt_translation = std::min(std::min(dxi[0],dxi[1]), dxi[2]) / PhysConst::c * parms->cfl_factor; } Real dt_flavor = 0.0; - if (flavor_cfl_factor > 0.0) { + if (parms->flavor_cfl_factor > 0.0 && parms->collision_cfl_factor > 0.0) { // define the reduction operator to get the max contribution to // the potential from matter and neutrinos // compute "effective" potential (ergs) that produces characteristic timescale @@ -42,10 +46,9 @@ Real compute_dt(const Geometry& geom, const Real cfl_factor, const MultiFab& sta ReduceOps reduce_op; ReduceData reduce_data(reduce_op); using ReduceTuple = typename decltype(reduce_data)::Type; - for (MFIter mfi(state); mfi.isValid(); ++mfi) - { + for (MFIter mfi(state); mfi.isValid(); ++mfi) { const Box& bx = mfi.fabbox(); - auto const& fab = state.array(mfi); + auto const& fab = state.array(mfi); reduce_op.eval(bx, reduce_data, [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple { @@ -53,25 +56,44 @@ Real compute_dt(const Geometry& geom, const Real cfl_factor, const MultiFab& sta #include "generated_files/Evolve.cpp_compute_dt_fill" return {V_adaptive, V_stupid}; }); - } + } + + // extract the reduced values from the combined reduced data structure + auto rv = reduce_data.value(); + Real Vmax_adaptive = amrex::get<0>(rv) + FlavoredNeutrinoContainer::Vvac_max; + Real Vmax_stupid = amrex::get<1>(rv) + FlavoredNeutrinoContainer::Vvac_max; + + // reduce across MPI ranks + ParallelDescriptor::ReduceRealMax(Vmax_adaptive); + ParallelDescriptor::ReduceRealMax(Vmax_stupid ); - // extract the reduced values from the combined reduced data structure - auto rv = reduce_data.value(); - Real Vmax_adaptive = amrex::get<0>(rv) + FlavoredNeutrinoContainer::Vvac_max; - Real Vmax_stupid = amrex::get<1>(rv) + FlavoredNeutrinoContainer::Vvac_max; + // define the dt associated with each method + Real dt_flavor_adaptive = std::numeric_limits::max(); + Real dt_flavor_stupid = std::numeric_limits::max(); + Real dt_flavor_absorption = std::numeric_limits::max(); // Initialize with infinity - // reduce across MPI ranks - ParallelDescriptor::ReduceRealMax(Vmax_adaptive); - ParallelDescriptor::ReduceRealMax(Vmax_stupid ); + if (parms->attenuation_hamiltonians != 0) { + dt_flavor_adaptive = PhysConst::hbar / Vmax_adaptive * parms->flavor_cfl_factor / parms->attenuation_hamiltonians; + dt_flavor_stupid = PhysConst::hbar / Vmax_stupid * parms->flavor_cfl_factor / parms->attenuation_hamiltonians; + } - // define the dt associated with each method - Real dt_flavor_adaptive = PhysConst::hbar/Vmax_adaptive*flavor_cfl_factor; - Real dt_flavor_stupid = PhysConst::hbar/Vmax_stupid *flavor_cfl_factor; + if (parms->IMFP_method == 1) { + // Use the IMFPs from the input file and find the maximum absorption IMFP + double max_IMFP_abs = std::numeric_limits::lowest(); // Initialize max to lowest possible value + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < NUM_FLAVORS; ++j) { + max_IMFP_abs = std::max(max_IMFP_abs, parms->IMFP_abs[i][j]); + } + } + // Calculate dt_flavor_absorption + dt_flavor_absorption = (1 / (PhysConst::c * max_IMFP_abs)) * parms->collision_cfl_factor; + } - // pick the appropriate timestep - dt_flavor = dt_flavor_stupid; - if(max_adaptive_speedup>1) - dt_flavor = min(dt_flavor_stupid*max_adaptive_speedup, dt_flavor_adaptive); + // pick the appropriate timestep + dt_flavor = min(dt_flavor_stupid, dt_flavor_adaptive, dt_flavor_absorption); + if(parms->max_adaptive_speedup>1) { + dt_flavor = min(dt_flavor_stupid*parms->max_adaptive_speedup, dt_flavor_adaptive, dt_flavor_absorption); + } } Real dt = 0.0; @@ -92,6 +114,7 @@ void deposit_to_mesh(const FlavoredNeutrinoContainer& neutrinos, MultiFab& state { const auto plo = geom.ProbLoArray(); const auto dxi = geom.InvCellSizeArray(); + const Real inv_cell_volume = dxi[0]*dxi[1]*dxi[2]; // Create an alias of the MultiFab so ParticleToMesh only erases the quantities // that will be set by the neutrinos. @@ -129,16 +152,115 @@ void interpolate_rhs_from_mesh(FlavoredNeutrinoContainer& neutrinos_rhs, const M { const auto plo = geom.ProbLoArray(); const auto dxi = geom.InvCellSizeArray(); - const Real inv_cell_volume = dxi[0]*dxi[1]*dxi[2]; const int shape_factor_order_x = geom.Domain().length(0) > 1 ? SHAPE_FACTOR_ORDER : 0; const int shape_factor_order_y = geom.Domain().length(1) > 1 ? SHAPE_FACTOR_ORDER : 0; const int shape_factor_order_z = geom.Domain().length(2) > 1 ? SHAPE_FACTOR_ORDER : 0; + + //Create EoS table object + using namespace nuc_eos_private; + EOS_tabulated EOS_tabulated_obj(alltables, epstable, logrho, logtemp, + yes, helperVarsReal, helperVarsInt); + + //Create NuLib table object + using namespace nulib_private; + NuLib_tabulated NuLib_tabulated_obj(alltables_nulib, logrho_nulib, logtemp_nulib, + yes_nulib, helperVarsReal_nulib, helperVarsInt_nulib); + + + +//The following commented loop can be used to print information about each particle in case debug is needed in future. +/* + const int lev = 0; +#ifdef _OPENMP +#pragma omp parallel +#endif + for (FNParIter pti(neutrinos_rhs, lev); pti.isValid(); ++pti) + { + const int np = pti.numParticles(); + FlavoredNeutrinoContainer::ParticleType* pstruct = &(pti.GetArrayOfStructs()[0]); + amrex::ParallelFor (np, [=] AMREX_GPU_DEVICE (int i) { + FlavoredNeutrinoContainer::ParticleType& p = pstruct[i]; + //printf("(Inside Evolve.cpp) Partile i = %d, Vphase = %g \n", i, p.rdata(PIdx::Vphase)); + }); + } +*/ + + + amrex::MeshToParticle(neutrinos_rhs, state, 0, [=] AMREX_GPU_DEVICE (FlavoredNeutrinoContainer::ParticleType& p, amrex::Array4 const& sarr) { + + // If statement to avoid computing quantities of particles inside the black hole. + if( parms->do_blackhole==1 ){ + + // Compute particle distance from black hole center + double particle_distance_from_bh_center = sqrt(amrex::Math::powi<2>(p.rdata(PIdx::x) - parms->bh_center_x) + + amrex::Math::powi<2>(p.rdata(PIdx::y) - parms->bh_center_y) + + amrex::Math::powi<2>(p.rdata(PIdx::z) - parms->bh_center_z)); // cm + // Set time derivatives to zero if particles is inside the BH + if ( particle_distance_from_bh_center < parms->bh_radius ) { + + // set the dx/dt values + p.rdata(PIdx::x) = p.rdata(PIdx::pupx) / p.rdata(PIdx::pupt) * PhysConst::c; + p.rdata(PIdx::y) = p.rdata(PIdx::pupy) / p.rdata(PIdx::pupt) * PhysConst::c; + p.rdata(PIdx::z) = p.rdata(PIdx::pupz) / p.rdata(PIdx::pupt) * PhysConst::c; + // set the dt/dt = 1. Neutrinos move at one second per second + p.rdata(PIdx::time) = 1.0; + // set the d(pE)/dt values + p.rdata(PIdx::pupx) = 0; + p.rdata(PIdx::pupy) = 0; + p.rdata(PIdx::pupz) = 0; + // set the dE/dt values + p.rdata(PIdx::pupt) = 0; + // set the dVphase/dt values + p.rdata(PIdx::Vphase) = 0; + + // Set the dN/dt and dNbar/dt values to zero + #include "generated_files/Evolve.cpp_dfdt_fill_zeros" + + return; + } + } + + // Periodic empty boundary conditions. + // Set time derivatives to zero if particles are in the boundary cells + // Check if periodic empty boundary conditions are enabled + if (parms->do_periodic_empty_bc == 1) { + + // Check if the particle is in the boundary cells + if (p.rdata(PIdx::x) < parms->Lx / parms->ncell[0] || + p.rdata(PIdx::x) > parms->Lx - parms->Lx / parms->ncell[0] || + p.rdata(PIdx::y) < parms->Ly / parms->ncell[1] || + p.rdata(PIdx::y) > parms->Ly - parms->Ly / parms->ncell[1] || + p.rdata(PIdx::z) < parms->Lz / parms->ncell[2] || + p.rdata(PIdx::z) > parms->Lz - parms->Lz / parms->ncell[2] ) { + + // set the dx/dt values + p.rdata(PIdx::x) = p.rdata(PIdx::pupx) / p.rdata(PIdx::pupt) * PhysConst::c; + p.rdata(PIdx::y) = p.rdata(PIdx::pupy) / p.rdata(PIdx::pupt) * PhysConst::c; + p.rdata(PIdx::z) = p.rdata(PIdx::pupz) / p.rdata(PIdx::pupt) * PhysConst::c; + // set the dt/dt = 1. Neutrinos move at one second per second + p.rdata(PIdx::time) = 1.0; + // set the d(pE)/dt values + p.rdata(PIdx::pupx) = 0; + p.rdata(PIdx::pupy) = 0; + p.rdata(PIdx::pupz) = 0; + // set the dE/dt values + p.rdata(PIdx::pupt) = 0; + // set the dVphase/dt values + p.rdata(PIdx::Vphase) = 0; + + // Set the dN/dt and dNbar/dt values to zero + #include "generated_files/Evolve.cpp_dfdt_fill_zeros" + + return; + } + } + #include "generated_files/Evolve.cpp_Vvac_fill" const amrex::Real delta_x = (p.pos(0) - plo[0]) * dxi[0]; @@ -149,6 +271,11 @@ void interpolate_rhs_from_mesh(FlavoredNeutrinoContainer& neutrinos_rhs, const M const ParticleInterpolator sy(delta_y, shape_factor_order_y); const ParticleInterpolator sz(delta_z, shape_factor_order_z); + // The following variables contains temperature, electron fraction, and density interpolated from grid quantities to particle positions + Real T_pp = 0; // erg + Real Ye_pp = 0; + Real rho_pp = 0; // g/ccm + for (int k = sz.first(); k <= sz.last(); ++k) { for (int j = sy.first(); j <= sy.last(); ++j) { for (int i = sx.first(); i <= sx.last(); ++i) { @@ -157,20 +284,215 @@ void interpolate_rhs_from_mesh(FlavoredNeutrinoContainer& neutrinos_rhs, const M } } - // set the dfdt values into p.rdata + // Declare matrices to be used in quantum kinetic equation calculation + Real IMFP_abs[NUM_FLAVORS][NUM_FLAVORS]; // Neutrino inverse mean free path matrix for nucleon absortion: diag( k_e , k_u , k_t ) + Real IMFP_absbar[NUM_FLAVORS][NUM_FLAVORS]; // Antineutrino inverse mean free path matrix for nucleon absortion: diag( kbar_e , kbar_u , kbar_t ) + Real IMFP_scat[NUM_FLAVORS][NUM_FLAVORS]; // Neutrino inverse mean free path matrix for scatteting: diag( k_e , k_u , k_t ) + Real IMFP_scatbar[NUM_FLAVORS][NUM_FLAVORS]; // Antineutrino inverse mean free path matrix for scatteting: diag( kbar_e , kbar_u , kbar_t ) + Real f_eq[NUM_FLAVORS][NUM_FLAVORS]; // Neutrino equilibrium Fermi-dirac distribution matrix: f_eq = diag( f_e , f_u , f_t ) + Real f_eqbar[NUM_FLAVORS][NUM_FLAVORS]; // Antineutrino equilibrium Fermi-dirac distribution matrix: f_eq = diag( fbar_e , fbar_u , fbar_t ) + Real munu[NUM_FLAVORS][NUM_FLAVORS]; // Neutrino chemical potential matrix: munu = diag ( munu_e , munu_x) + Real munubar[NUM_FLAVORS][NUM_FLAVORS]; // Antineutrino chemical potential matrix: munu = diag ( munubar_e , munubar_x) + + // Initialize matrices with zeros + for (int i=0; iIMFP_method==1){ + for (int i=0; iIMFP_abs[0][i]; // 1/cm : Read absorption inverse mean free path from input parameters file. + IMFP_absbar[i][i] = parms->IMFP_abs[1][i]; // 1/cm : Read absorption inverse mean free path from input parameters file. + munu[i][i] = parms->munu[0][i]; // ergs : Read neutrino chemical potential from input parameters file. + munubar[i][i] = parms->munu[1][i]; // ergs : Read antineutrino chemical potential from input parameters file. + + } + } + // If opacity_method is 2, the code interpolate inverse mean free paths from NuLib table and electron neutrino chemical potential from EoS table to compute the collision term. + else if(parms->IMFP_method==2){ + + // Assign temperature, electron fraction, and density at the particle's position to new variables for interpolation of chemical potentials and inverse mean free paths. + Real rho = rho_pp; // Density of background matter at this particle's position g/cm^3 + Real temperature = T_pp / (1e6*CGSUnitsConst::eV); // Temperature of background matter at this particle's position 0.05 //MeV + Real Ye = Ye_pp; // Electron fraction of background matter at this particle's position + + //-------------------- Values from EoS table ------------------------------ + double mue_out, muhat_out; // mue_out : Electron chemical potential. muhat_out : neutron minus proton chemical potential + int keyerr, anyerr; + EOS_tabulated_obj.get_mue_muhat(rho, temperature, Ye, mue_out, muhat_out, keyerr, anyerr); + if (anyerr) assert(0); //If there is an error in interpolation call, stop execution. + +//#define DEBUG_INTERPOLATION_TABLES +#ifdef DEBUG_INTERPOLATION_TABLES + printf("(Evolve.cpp) mu_e interpolated = %f\n", mue_out); + printf("(Evolve.cpp) muhat interpolated = %f\n", muhat_out); +#endif + // munu_val : electron neutrino chemical potential + const double munu_val = ( mue_out - muhat_out ) * 1e6*CGSUnitsConst::eV ; //munu -> "mu_e" - "muhat" + + munu[0][0] = munu_val; // erg : Save neutrino chemical potential from EOS table in chemical potential matrix + munubar[0][0] = -1.0 * munu_val; // erg : Save antineutrino chemical potential from EOS table in chemical potential matrix + + //--------------------- Values from NuLib table --------------------------- + double *helperVarsReal_nulib = NuLib_tabulated_obj.get_helperVarsReal_nulib(); + int idx_group = NULIBVAR(idx_group); + //FIXME: specify neutrino energy using the following: + // double neutrino_energy = p.rdata(PIdx::pupt); locate energy bin using this. + + //idx_species = {0 for electron neutrino, 1 for electron antineutrino and 2 for all other heavier ones} + //electron neutrino: [0, 0] + int idx_species = 0; + double absorption_opacity, scattering_opacity; + NuLib_tabulated_obj.get_opacities(rho, temperature, Ye, absorption_opacity, scattering_opacity, + keyerr, anyerr, idx_species, idx_group); + if (anyerr) assert(0); + +#ifdef DEBUG_INTERPOLATION_TABLES + printf("(Evolve.cpp) absorption_opacity[e] interpolated = %17.6g\n", absorption_opacity); + printf("(Evolve.cpp) scattering_opacity[e] interpolated = %17.6g\n", scattering_opacity); +#endif + + IMFP_abs[0][0] = absorption_opacity; + IMFP_scat[0][0] = scattering_opacity; + + //electron antineutrino: [1, 0] + idx_species = 1; + NuLib_tabulated_obj.get_opacities(rho, temperature, Ye, absorption_opacity, scattering_opacity, + keyerr, anyerr, idx_species, idx_group); + if (anyerr) assert(0); + +#ifdef DEBUG_INTERPOLATION_TABLES + printf("(Evolve.cpp) absorption_opacity[a] interpolated = %17.6g\n", absorption_opacity); + printf("(Evolve.cpp) scattering_opacity[a] interpolated = %17.6g\n", scattering_opacity); +#endif + + IMFP_absbar[0][0] = absorption_opacity; + IMFP_scatbar[0][0] = scattering_opacity; + + //heavier ones: muon neutrino[0,1], muon antineutruino[1,1], tau neutrino[0,2], tau antineutrino[1,2] + idx_species = 2; + NuLib_tabulated_obj.get_opacities(rho, temperature, Ye, absorption_opacity, scattering_opacity, + keyerr, anyerr, idx_species, idx_group); + if (anyerr) assert(0); + +#ifdef DEBUG_INTERPOLATION_TABLES + printf("(Evolve.cpp) absorption_opacity[x] interpolated = %17.6g\n", absorption_opacity); + printf("(Evolve.cpp) scattering_opacity[x] interpolated = %17.6g\n", scattering_opacity); +#endif + + for (int i=1; ineutrino or 1->antineutrino + // for(int j=1; jelectron, 1->heavy(muon), 2->heavy(tau); all heavy same for current table + IMFP_abs[i][i] = absorption_opacity ; // ... fix it ... + IMFP_absbar[i][i] = absorption_opacity ; // ... fix it ... + IMFP_scat[i][i] = scattering_opacity ; // ... fix it ... + IMFP_scatbar[i][i] = scattering_opacity ; // ... fix it ... + // } + } + //----------------------------------------------------------------------- + } + else AMREX_ASSERT_WITH_MESSAGE(false, "only available opacity_method is 0, 1 or 2"); + + // Compute equilibrium distribution functions and include Pauli blocking term if requested + if(parms->IMFP_method==1 || parms->IMFP_method==2){ + + for (int i=0; iDo_Pauli_blocking == 1){ + IMFP_abs[i][i] = IMFP_abs[i][i] / ( 1 - f_eq[i][i] ) ; // Multiply the absortion inverse mean free path by the Pauli blocking term 1 / (1 - f_eq). + IMFP_absbar[i][i] = IMFP_absbar[i][i] / ( 1 - f_eqbar[i][i] ) ; // Multiply the absortion inverse mean free path by the Pauli blocking term 1 / (1 - f_eq). + } + } + } + // Compute the time derivative of \( N_{ab} \) using the Quantum Kinetic Equations (QKE). + #include "generated_files/Evolve.cpp_dfdt_fill" + + // set the dx/dt values p.rdata(PIdx::x) = p.rdata(PIdx::pupx) / p.rdata(PIdx::pupt) * PhysConst::c; p.rdata(PIdx::y) = p.rdata(PIdx::pupy) / p.rdata(PIdx::pupt) * PhysConst::c; p.rdata(PIdx::z) = p.rdata(PIdx::pupz) / p.rdata(PIdx::pupt) * PhysConst::c; - p.rdata(PIdx::time) = 1.0; // neutrinos move at one second per second! + // set the dt/dt = 1. Neutrinos move at one second per second + p.rdata(PIdx::time) = 1.0; + // set the d(pE)/dt values p.rdata(PIdx::pupx) = 0; p.rdata(PIdx::pupy) = 0; p.rdata(PIdx::pupz) = 0; + // set the dE/dt values p.rdata(PIdx::pupt) = 0; - p.rdata(PIdx::N) = 0; - p.rdata(PIdx::Nbar) = 0; - p.rdata(PIdx::L) = 0; - p.rdata(PIdx::Lbar) = 0; + // set the dVphase/dt values + p.rdata(PIdx::Vphase) = 0; - #include "generated_files/Evolve.cpp_dfdt_fill" }); } + + +/** + * @brief Sets the N and Nbar to zero for particles inside the black hole or boundary cells. + * + * This function iterates over all particles in the `FlavoredNeutrinoContainer` and sets N and Nbar to zero if pariticles are inside the black hole or within the boundary cells of the simulation domain. + * + * @param neutrinos Reference to the container holding the flavored neutrinos. + * @param parms Pointer to the structure containing test parameters, including black hole properties and domain dimensions. + * + * The function performs the following steps: + * - Iterates over all particles in the container. + * - Computes the distance of each particle from the black hole center. + * - Sets N and Nbar to zero if the particle is inside the black hole radius. + * - Sets N and Nbar to zero if the particle is within the boundary cells of the simulation domain. + * + */ +void empty_particles_at_boundary_cells(FlavoredNeutrinoContainer& neutrinos, const TestParams* parms) +{ + + const int lev = 0; + for (FNParIter pti(neutrinos, lev); pti.isValid(); ++pti) + { + const int np = pti.numParticles(); + FlavoredNeutrinoContainer::ParticleType* pstruct = &(pti.GetArrayOfStructs()[0]); + + amrex::ParallelFor (np, [=] AMREX_GPU_DEVICE (int i) { + FlavoredNeutrinoContainer::ParticleType& p = pstruct[i]; + + // Check if the simulation involves a black hole somewhere in the domain + if(parms->do_blackhole==1 ){ + + // Compute particle distance from black hole center + double particle_distance_from_bh_center = sqrt(amrex::Math::powi<2>(p.rdata(PIdx::x) - parms->bh_center_x) + + amrex::Math::powi<2>(p.rdata(PIdx::y) - parms->bh_center_y) + + amrex::Math::powi<2>(p.rdata(PIdx::z) - parms->bh_center_z)); // cm + + // Set time derivatives to zero if particles are inside the black hole + if ( particle_distance_from_bh_center < parms->bh_radius ) { + #include "generated_files/Evolve.cpp_dfdt_fill_zeros" + return; + } + + } + + // Set time derivatives to zero if particles are within the boundary cells + if (p.rdata(PIdx::x) < parms->Lx / parms->ncell[0] || + p.rdata(PIdx::x) > parms->Lx - parms->Lx / parms->ncell[0] || + p.rdata(PIdx::y) < parms->Ly / parms->ncell[1] || + p.rdata(PIdx::y) > parms->Ly - parms->Ly / parms->ncell[1] || + p.rdata(PIdx::z) < parms->Lz / parms->ncell[2] || + p.rdata(PIdx::z) > parms->Lz - parms->Lz / parms->ncell[2] ) { + + #include "generated_files/Evolve.cpp_dfdt_fill_zeros" + return; + } + }); + } +} diff --git a/Source/FlavoredNeutrinoContainer.H b/Source/FlavoredNeutrinoContainer.H index cb6bcd68..0dcb4d98 100644 --- a/Source/FlavoredNeutrinoContainer.H +++ b/Source/FlavoredNeutrinoContainer.H @@ -61,6 +61,9 @@ public: } }; +//Create an enum to pass as a template argument to the particle creation function. +enum BoundaryParticleCreationDirection {I_PLUS, I_MINUS, J_PLUS, J_MINUS, K_PLUS, K_MINUS}; + class FlavoredNeutrinoContainer : public amrex::ParticleContainer { @@ -76,6 +79,9 @@ public: void InitParticles(const TestParams* parms); + template + void CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); + void SyncLocation(int type); void UpdateLocationFrom(FlavoredNeutrinoContainer& Other); @@ -89,8 +95,6 @@ public: Redistribute(lev_min, lev_max, nGrow, local); } - void Renormalize(const TestParams* parms); - amrex::Vector get_attribute_names() const { return attribute_names; @@ -100,4 +104,5 @@ public: static ApplyFlavoredNeutrinoRHS particle_apply_rhs; }; + #endif diff --git a/Source/FlavoredNeutrinoContainer.cpp b/Source/FlavoredNeutrinoContainer.cpp index ee050e2d..6abd1907 100644 --- a/Source/FlavoredNeutrinoContainer.cpp +++ b/Source/FlavoredNeutrinoContainer.cpp @@ -1,7 +1,5 @@ #include "FlavoredNeutrinoContainer.H" #include "Constants.H" -#include -#include using namespace amrex; @@ -84,30 +82,4 @@ UpdateLocationFrom(FlavoredNeutrinoContainer& Ploc) } }); } -} - -void FlavoredNeutrinoContainer:: -Renormalize(const TestParams* parms) -{ - BL_PROFILE("FlavoredNeutrinoContainer::Renormalize"); - - const int lev = 0; - - const auto dxi = Geom(lev).InvCellSizeArray(); - const auto plo = Geom(lev).ProbLoArray(); - -#ifdef _OPENMP -#pragma omp parallel -#endif - for (FNParIter pti(*this, lev); pti.isValid(); ++pti) - { - const int np = pti.numParticles(); - ParticleType * pstruct = &(pti.GetArrayOfStructs()[0]); - - amrex::ParallelFor (np, [=] AMREX_GPU_DEVICE (int i) { - ParticleType& p = pstruct[i]; - Real sumP, length, error; - #include "generated_files/FlavoredNeutrinoContainer.cpp_Renormalize_fill" - }); - } -} +} \ No newline at end of file diff --git a/Source/FlavoredNeutrinoContainerInit.cpp b/Source/FlavoredNeutrinoContainerInit.cpp index 3014b623..15362e1d 100644 --- a/Source/FlavoredNeutrinoContainerInit.cpp +++ b/Source/FlavoredNeutrinoContainerInit.cpp @@ -1,661 +1,767 @@ #include "FlavoredNeutrinoContainer.H" - #include "Constants.H" +#include "Constants.H" #include +#include +#include using namespace amrex; -// generate an array of theta,phi pairs that uniformily cover the surface of a sphere -// based on DOI: 10.1080/10586458.2003.10504492 section 3.3 but specifying n_j=0 instead of n -Gpu::ManagedVector > uniform_sphere_xyz(int nphi_at_equator){ - AMREX_ASSERT(nphi_at_equator>0); - - Real dtheta = M_PI*std::sqrt(3)/nphi_at_equator; - - Gpu::ManagedVector > xyz; - Real theta = 0; - Real phi0 = 0; - while(theta < M_PI/2.){ - int nphi = theta==0 ? nphi_at_equator : lround(nphi_at_equator * std::cos(theta)); - Real dphi = 2.*M_PI/nphi; - if(nphi==1) theta = M_PI/2.; - - for(int iphi=0; iphi{x,y,z}); - // construct exactly opposing vectors to limit subtractive cancellation errors - // and be able to represent isotropy exactly (all odd moments == 0) - if(theta>0) xyz.push_back(GpuArray{-x,-y,-z}); - } - theta += dtheta; - phi0 = phi0 + 0.5*dphi; // offset by half step so adjacent latitudes are not always aligned in longitude - } - - return xyz; -} +//=========================================// +// Particle distribution in momentum space // +//=========================================// +/** + * @brief Reads the input file containing the initial conditions of the particles. + * + * This function reads the particle data from a file generated using the input Python scripts + * in the directory "Scripts/initial_conditions/---.py" and stores the momentum, energy, and flavor + * occupation matrices for neutrinos and antineutrinos. The file is expected to follow a specific + * format, with the number of flavors in the first line, followed by particle data in each + * subsequent line in the following order (2-flavor case): E*phatx, E*phaty, E*phatz, E, + * N00_Re, N01_Re, N01_Im, N11_Re, N00_Rebar, N01_Rebar, N01_Imbar, N11_Rebar, TrHN, Vphase. + * This can be generalized to the 3-flavor case. + * + * @param filename The name of the input file containing the particle data. + * + * @return A managed vector of GpuArray containing the particle information. + * Each GpuArray stores particle attributes: E*phatx, E*phaty, E*phatz, E, + * N00_Re, N01_Re, N01_Im, N11_Re, N00_Rebar, N01_Rebar, N01_Imbar, N11_Rebar, + * TrHN, Vphase. + * + * @note The file should contain the number of neutrino flavors in the first line, + * which must match the value that Emu was compiled with. If the number of flavors + * does not match, the function will print an error message and terminate execution. + */ +Gpu::ManagedVector> read_particle_data(std::string filename){ + + // This array will save the particles information + Gpu::ManagedVector> particle_data; + + // open the file as a stream + std::ifstream file(filename); + + // temporary string/stream + std::string line; + std::stringstream ss; + + // create zero particle + GpuArray temp_particle; + for(int i=0; i> NF_in; + if(NF_in != NUM_FLAVORS) amrex::Print() << "Error: number of flavors in particle data file does not match the number of flavors Emu was compiled for." << std::endl; + AMREX_ASSERT(NF_in == NUM_FLAVORS); + + // Loop over every line in the initial condition file. + // This is equivalent to looping over every particle. + // Save every particle's information in the array particle_data. + while(std::getline(file, line)){ + ss = std::stringstream(line); + // skip over the first four attributes (x,y,z,t) + for(int i=4; i> temp_particle[i]; + particle_data.push_back(temp_particle); + } -// residual for the root finder -// Z needs to be bigger if residual is positive -// Minerbo (1978) (unfortunately under Elsevier paywall) -// Can also see Richers (2020) https://ui.adsabs.harvard.edu/abs/2020PhRvD.102h3017R -// Eq.41 (where a is Z), but in the non-degenerate limit -// k->0, eta->0, N->Z/(4pi sinh(Z)) (just to make it integrate to 1) -// minerbo_residual is the "f" equation between eq.42 and 43 -Real minerbo_residual(const Real fluxfac, const Real Z){ - return fluxfac - 1.0/std::tanh(Z) + 1.0 / Z; -} -Real minerbo_residual_derivative(const Real fluxfac, const Real Z){ - return 1.0/(std::sinh(Z)*std::sinh(Z)) - 1.0/(Z*Z); -} -Real minerbo_Z(const Real fluxfac){ - // hard-code in these parameters because they are not - // really very important... - Real maxresidual = 1e-6; - Real maxcount = 20; - Real minfluxfac = 1e-3; - - // set the initial conditions - Real Z = 1.0; - - // catch the small flux factor case to prevent nans - if(fluxfac < minfluxfac) - Z = 3.*fluxfac; - else{ - Real residual = 1.0; - int count = 0; - while(std::abs(residual)>maxresidual and countmaxresidual) - amrex::Error("Failed to converge on a solution."); - } - - amrex::Print() << "fluxfac="<nppc[0], - *parms->nppc[1], - *parms->nppc[2]); + const int nlocs_per_cell = AMREX_D_TERM( parms->nppc[0], + *parms->nppc[1], + *parms->nppc[2]); + + // array of direction vectors + Gpu::ManagedVector > particle_data = read_particle_data(parms->particle_data_filename); + auto* particle_data_p = particle_data.dataPtr(); - Gpu::ManagedVector > direction_vectors = uniform_sphere_xyz(parms->nphi_equator); - auto* direction_vectors_p = direction_vectors.dataPtr(); - int ndirs_per_loc = direction_vectors.size(); - amrex::Print() << "Using " << ndirs_per_loc << " directions based on " << parms->nphi_equator << " directions at the equator." << std::endl; - - const Real scale_fac = dx[0]*dx[1]*dx[2]/nlocs_per_cell/ndirs_per_loc; - - // get the Z parameters for the Minerbo closure if using simuation type 5 - Real Ze, Za, Zx; - Real fluxfac_e, fluxfac_a, fluxfac_x; - if(parms->simulation_type==5){ - fluxfac_e = std::sqrt( - parms->st5_fxnue*parms->st5_fxnue + - parms->st5_fynue*parms->st5_fynue + - parms->st5_fznue*parms->st5_fznue ); - fluxfac_a = std::sqrt( - parms->st5_fxnua*parms->st5_fxnua + - parms->st5_fynua*parms->st5_fynua + - parms->st5_fznua*parms->st5_fznua ); - fluxfac_x = std::sqrt( - parms->st5_fxnux*parms->st5_fxnux + - parms->st5_fynux*parms->st5_fynux + - parms->st5_fznux*parms->st5_fznux ); - Ze = minerbo_Z(fluxfac_e); - Za = minerbo_Z(fluxfac_a); - Zx = minerbo_Z(fluxfac_x); - } + // determine the number of directions per location + int ndirs_per_loc = particle_data.size(); + amrex::Print() << "Using " << ndirs_per_loc << " directions." << std::endl; + const Real scale_fac = dx[0]*dx[1]*dx[2]/nlocs_per_cell; + + // Loop over multifabs // #ifdef _OPENMP #pragma omp parallel #endif - for (MFIter mfi = MakeMFIter(lev); mfi.isValid(); ++mfi) + for (MFIter mfi = MakeMFIter(lev); mfi.isValid(); ++mfi) { - const Box& tile_box = mfi.tilebox(); + const Box& tile_box = mfi.tilebox(); - const auto lo = amrex::lbound(tile_box); - const auto hi = amrex::ubound(tile_box); + const auto lo = amrex::lbound(tile_box); + const auto hi = amrex::ubound(tile_box); - Gpu::ManagedVector counts(tile_box.numPts(), 0); - unsigned int* pcount = counts.dataPtr(); + Gpu::ManagedVector counts(tile_box.numPts(), 0); + unsigned int* pcount = counts.dataPtr(); - Gpu::ManagedVector offsets(tile_box.numPts()); - unsigned int* poffset = offsets.dataPtr(); + Gpu::ManagedVector offsets(tile_box.numPts()); + unsigned int* poffset = offsets.dataPtr(); - // Determine how many particles to add to the particle tile per cell - amrex::ParallelFor(tile_box, - [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept - { - for (int i_part=0; i_partnppc, i_part); + get_position_unit_cell(r, parms->nppc, i_part); - Real x = plo[0] + (i + r[0])*dx[0]; - Real y = plo[1] + (j + r[1])*dx[1]; - Real z = plo[2] + (k + r[2])*dx[2]; + Real x = plo[0] + (i + r[0])*dx[0]; + Real y = plo[1] + (j + r[1])*dx[1]; + Real z = plo[2] + (k + r[2])*dx[2]; - if (x >= a_bounds.hi(0) || x < a_bounds.lo(0) || - y >= a_bounds.hi(1) || y < a_bounds.lo(1) || - z >= a_bounds.hi(2) || z < a_bounds.lo(2) ) continue; + if (x >= a_bounds.hi(0) || x < a_bounds.lo(0) || + y >= a_bounds.hi(1) || y < a_bounds.lo(1) || + z >= a_bounds.hi(2) || z < a_bounds.lo(2) ) continue; - int ix = i - lo.x; - int iy = j - lo.y; - int iz = k - lo.z; - int nx = hi.x-lo.x+1; - int ny = hi.y-lo.y+1; - int nz = hi.z-lo.z+1; - unsigned int uix = amrex::min(nx-1,amrex::max(0,ix)); - unsigned int uiy = amrex::min(ny-1,amrex::max(0,iy)); - unsigned int uiz = amrex::min(nz-1,amrex::max(0,iz)); - unsigned int cellid = (uix * ny + uiy) * nz + uiz; - pcount[cellid] += ndirs_per_loc; - } - }); - - // Determine total number of particles to add to the particle tile - Gpu::inclusive_scan(counts.begin(), counts.end(), offsets.begin()); - - int num_to_add = offsets[tile_box.numPts()-1]; - if (num_to_add == 0) continue; - - // this will be the particle ID for the first new particle in the tile - long new_pid; - ParticleType* pstruct; - #ifdef _OPENMP - #pragma omp critical - #endif - { - - auto& particles = GetParticles(lev); - auto& particle_tile = particles[std::make_pair(mfi.index(), mfi.LocalTileIndex())]; - - // Resize the particle container - auto old_size = particle_tile.GetArrayOfStructs().size(); - auto new_size = old_size + num_to_add; - particle_tile.resize(new_size); - - // get the next particle ID - new_pid = ParticleType::NextID(); - - // set the starting particle ID for the next tile of particles - ParticleType::NextID(new_pid + num_to_add); - - pstruct = particle_tile.GetArrayOfStructs()().data(); - } - - int procID = ParallelDescriptor::MyProc(); - - Real domain_length_z = Geom(lev).ProbLength(2); - - // Initialize particle data in the particle tile - amrex::ParallelFor(tile_box, - [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept - { - int ix = i - lo.x; - int iy = j - lo.y; - int iz = k - lo.z; - int nx = hi.x-lo.x+1; - int ny = hi.y-lo.y+1; - int nz = hi.z-lo.z+1; - unsigned int uix = amrex::min(nx-1,amrex::max(0,ix)); - unsigned int uiy = amrex::min(ny-1,amrex::max(0,iy)); - unsigned int uiz = amrex::min(nz-1,amrex::max(0,iz)); - unsigned int cellid = (uix * ny + uiy) * nz + uiz; - - for (int i_loc=0; i_locnppc, i_loc); + get_position_unit_cell(r, parms->nppc, i_loc); - Real x = plo[0] + (i + r[0])*dx[0]; - Real y = plo[1] + (j + r[1])*dx[1]; - Real z = plo[2] + (k + r[2])*dx[2]; + Real x = plo[0] + (i + r[0])*dx[0]; + Real y = plo[1] + (j + r[1])*dx[1]; + Real z = plo[2] + (k + r[2])*dx[2]; - if (x >= a_bounds.hi(0) || x < a_bounds.lo(0) || - y >= a_bounds.hi(1) || y < a_bounds.lo(1) || - z >= a_bounds.hi(2) || z < a_bounds.lo(2) ) continue; + if (x >= a_bounds.hi(0) || x < a_bounds.lo(0) || + y >= a_bounds.hi(1) || y < a_bounds.lo(1) || + z >= a_bounds.hi(2) || z < a_bounds.lo(2) ) continue; - - for(int i_direction=0; i_direction u = direction_vectors_p[i_direction]; - //get_random_direction(u); - - //=========================// - // VACUUM OSCILLATION TEST // - //=========================// - if(parms->simulation_type==0){ - // set all particles to start in electron state (and anti-state) - // Set N to be small enough that self-interaction is not important - // Set all particle momenta to be such that one oscillation wavelength is 1cm - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // Set particle flavor - p.rdata(PIdx::N) = 1.0; - p.rdata(PIdx::Nbar) = 1.0; - p.rdata(PIdx::f00_Re) = 1.0; - p.rdata(PIdx::f01_Re) = 0.0; - p.rdata(PIdx::f01_Im) = 0.0; - p.rdata(PIdx::f11_Re) = 0.0; - p.rdata(PIdx::f00_Rebar) = 1.0; - p.rdata(PIdx::f01_Rebar) = 0.0; - p.rdata(PIdx::f01_Imbar) = 0.0; - p.rdata(PIdx::f11_Rebar) = 0.0; - -#if (NUM_FLAVORS==3) - p.rdata(PIdx::f22_Re) = 0.0; - p.rdata(PIdx::f22_Rebar) = 0.0; - p.rdata(PIdx::f02_Re) = 0.0; - p.rdata(PIdx::f02_Im) = 0.0; - p.rdata(PIdx::f12_Re) = 0.0; - p.rdata(PIdx::f12_Im) = 0.0; - p.rdata(PIdx::f02_Rebar) = 0.0; - p.rdata(PIdx::f02_Imbar) = 0.0; - p.rdata(PIdx::f12_Rebar) = 0.0; - p.rdata(PIdx::f12_Imbar) = 0.0; + for(int i_direction=0; i_direction= 0); + AMREX_ASSERT(p.rdata(PIdx::N11_Re ) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N00_Rebar) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N11_Rebar) >= 0); +#if NUM_FLAVORS==3 + AMREX_ASSERT(p.rdata(PIdx::N22_Re ) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N22_Rebar) >= 0); #endif - // set momentum so that a vacuum oscillation wavelength occurs over a distance of 1cm - // Set particle velocity to c in a random direction - Real dm2 = (parms->mass2-parms->mass1)*(parms->mass2-parms->mass1); //g^2 - p.rdata(PIdx::pupt) = dm2*PhysConst::c4 * sin(2.*parms->theta12) / (8.*M_PI*PhysConst::hbarc); // *1cm for units - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); + // Set particle position + p.pos(0) = x; + p.pos(1) = y; + p.pos(2) = z; + + // Set particle integrated position + p.rdata(PIdx::x) = x; + p.rdata(PIdx::y) = y; + p.rdata(PIdx::z) = z; + p.rdata(PIdx::time) = 0; + + // scale particle numbers based on number of points per cell and the cell volume + p.rdata(PIdx::N00_Re ) *= scale_fac; + p.rdata(PIdx::N11_Re ) *= scale_fac; + p.rdata(PIdx::N00_Rebar) *= scale_fac; + p.rdata(PIdx::N11_Rebar) *= scale_fac; +#if NUM_FLAVORS==3 + p.rdata(PIdx::N22_Re ) *= scale_fac; + p.rdata(PIdx::N22_Rebar) *= scale_fac; +#endif + + // Set phase space volume Vphase = dx^3 * dOmega * dE^3 / 3 + // From initial conditions, Vphase gets dOmega * dE^3 / 3 + // Here we multiply this value by the cell volume dx[0] * dx[1] * dx[2] + // Divide by the number of particle emission points inside the cell + p.rdata(PIdx::Vphase) *= scale_fac ; + + //=====================// + // Apply Perturbations // + //=====================// + if(parms->perturbation_type == 0){ + // random perturbations to the off-diagonals + Real rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N01_Re) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Re ) - p.rdata(PIdx::N11_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N01_Im) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Re ) - p.rdata(PIdx::N11_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N01_Rebar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Rebar) - p.rdata(PIdx::N11_Rebar)); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N01_Imbar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Rebar) - p.rdata(PIdx::N11_Rebar)); +#if NUM_FLAVORS==3 + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N02_Re) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Re ) - p.rdata(PIdx::N22_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N02_Im) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Re ) - p.rdata(PIdx::N22_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N12_Re) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N11_Re ) - p.rdata(PIdx::N22_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N12_Im) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N11_Re ) - p.rdata(PIdx::N22_Re )); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N02_Rebar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Rebar) - p.rdata(PIdx::N22_Rebar)); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N02_Imbar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N00_Rebar) - p.rdata(PIdx::N22_Rebar)); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N12_Rebar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N11_Rebar) - p.rdata(PIdx::N22_Rebar)); + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N12_Imbar) = parms->perturbation_amplitude*rand * (p.rdata(PIdx::N11_Rebar) - p.rdata(PIdx::N22_Rebar)); +#endif + } + if(parms->perturbation_type == 1){ + // Perturb real part of e-mu component only sinusoidally in z + Real nu_k = (2.*M_PI) / parms->perturbation_wavelength_cm; + p.rdata(PIdx::N01_Re) = parms->perturbation_amplitude*sin(nu_k*p.pos(2)) * (p.rdata(PIdx::N00_Re ) - p.rdata(PIdx::N11_Re )); + p.rdata(PIdx::N01_Rebar) = parms->perturbation_amplitude*sin(nu_k*p.pos(2)) * (p.rdata(PIdx::N00_Rebar) - p.rdata(PIdx::N11_Rebar)); + } + if(parms->perturbation_type == 2){ + // random perturbations of the diagonals + Real rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N00_Re) *= 1. + parms->perturbation_amplitude*rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N00_Rebar) *= 1. + parms->perturbation_amplitude*rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N11_Re) *= 1. + parms->perturbation_amplitude*rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N11_Rebar) *= 1. + parms->perturbation_amplitude*rand; +#if NUM_FLAVORS==3 + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N22_Re) *= 1. + parms->perturbation_amplitude*rand; + symmetric_uniform(&rand, engine); + p.rdata(PIdx::N22_Rebar) *= 1. + parms->perturbation_amplitude*rand; +#endif } - //==========================// - // BIPOLAR OSCILLATION TEST // - //==========================// - else if(parms->simulation_type==1){ - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // Set particle flavor - p.rdata(PIdx::f00_Re) = 1.0; - p.rdata(PIdx::f01_Re) = 0.0; - p.rdata(PIdx::f01_Im) = 0.0; - p.rdata(PIdx::f11_Re) = 0.0; - p.rdata(PIdx::f00_Rebar) = 1.0; - p.rdata(PIdx::f01_Rebar) = 0.0; - p.rdata(PIdx::f01_Imbar) = 0.0; - p.rdata(PIdx::f11_Rebar) = 0.0; - -#if (NUM_FLAVORS==3) - p.rdata(PIdx::f22_Re) = 0.0; - p.rdata(PIdx::f22_Rebar) = 0.0; - p.rdata(PIdx::f02_Re) = 0.0; - p.rdata(PIdx::f02_Im) = 0.0; - p.rdata(PIdx::f12_Re) = 0.0; - p.rdata(PIdx::f12_Im) = 0.0; - p.rdata(PIdx::f02_Rebar) = 0.0; - p.rdata(PIdx::f02_Imbar) = 0.0; - p.rdata(PIdx::f12_Rebar) = 0.0; - p.rdata(PIdx::f12_Imbar) = 0.0; -#endif + } // loop over direction + } // loop over location + }); // loop over grid cells + } // loop over multifabs - // set energy to 50 MeV to match Richers+(2019) - p.rdata(PIdx::pupt) = 50. * 1e6*CGSUnitsConst::eV; - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); - - // set particle weight such that density is - // 10 dm2 c^4 / (2 sqrt(2) GF E) - Real dm2 = (parms->mass2-parms->mass1)*(parms->mass2-parms->mass1); //g^2 - double omega = dm2*PhysConst::c4 / (2.*p.rdata(PIdx::pupt)); - double ndens = 10. * dm2*PhysConst::c4 / (2.*sqrt(2.) * PhysConst::GF * p.rdata(PIdx::pupt)); - double mu = sqrt(2.)*PhysConst::GF * ndens; - p.rdata(PIdx::N) = ndens * scale_fac; - p.rdata(PIdx::Nbar) = ndens * scale_fac; - } + // get the minimum neutrino energy for calculating the timestep + Real pupt_min = amrex::ReduceMin(*this, [=] AMREX_GPU_HOST_DEVICE (const FlavoredNeutrinoContainer::ParticleType& p) -> Real { return p.rdata(PIdx::pupt); }); + ParallelDescriptor::ReduceRealMin(pupt_min); +#include "generated_files/FlavoredNeutrinoContainerInit.cpp_Vvac_fill" +} // InitParticles() - //========================// - // 2-BEAM FAST FLAVOR TEST// - //========================// - else if(parms->simulation_type==2){ - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // Set particle flavor - p.rdata(PIdx::f00_Re) = 1.0; - p.rdata(PIdx::f01_Re) = 0.0; - p.rdata(PIdx::f01_Im) = 0.0; - p.rdata(PIdx::f11_Re) = 0.0; - p.rdata(PIdx::f00_Rebar) = 1.0; - p.rdata(PIdx::f01_Rebar) = 0.0; - p.rdata(PIdx::f01_Imbar) = 0.0; - p.rdata(PIdx::f11_Rebar) = 0.0; - -#if (NUM_FLAVORS==3) - p.rdata(PIdx::f22_Re) = 0.0; - p.rdata(PIdx::f22_Rebar) = 0.0; - p.rdata(PIdx::f02_Re) = 0.0; - p.rdata(PIdx::f02_Im) = 0.0; - p.rdata(PIdx::f12_Re) = 0.0; - p.rdata(PIdx::f12_Im) = 0.0; - p.rdata(PIdx::f02_Rebar) = 0.0; - p.rdata(PIdx::f02_Imbar) = 0.0; - p.rdata(PIdx::f12_Rebar) = 0.0; - p.rdata(PIdx::f12_Imbar) = 0.0; -#endif - // set energy to 50 MeV to match Richers+(2019) - p.rdata(PIdx::pupt) = 50. * 1e6*CGSUnitsConst::eV; - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); - - // set particle weight such that density is - // 0.5 dm2 c^4 / (2 sqrt(2) GF E) - // to get maximal growth according to Chakraborty 2016 Equation 2.10 - Real dm2 = (parms->mass2-parms->mass1)*(parms->mass2-parms->mass1); //g^2 - Real omega = dm2*PhysConst::c4 / (2.* p.rdata(PIdx::pupt)); - Real mu_ndens = sqrt(2.) * PhysConst::GF; // SI potential divided by the number density - double ndens = omega / (2.*mu_ndens); // want omega/2mu to be 1 - p.rdata(PIdx::N) = ndens * scale_fac * (1. + u[2]); - p.rdata(PIdx::Nbar) = ndens * scale_fac * (1. - u[2]); - } - //===============================// - // 3- k!=0 BEAM FAST FLAVOR TEST // - //===============================// - else if(parms->simulation_type==3){ - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // perturbation parameters - Real lambda = domain_length_z/(Real)parms->st3_wavelength_fraction_of_domain; - Real k = (2.*M_PI) / lambda; - - // Set particle flavor - p.rdata(PIdx::f00_Re) = 1.0; - p.rdata(PIdx::f01_Re) = parms->st3_amplitude*sin(k*p.pos(2)); - p.rdata(PIdx::f01_Im) = 0.0; - p.rdata(PIdx::f11_Re) = 0.0; - p.rdata(PIdx::f00_Rebar) = 1.0; - p.rdata(PIdx::f01_Rebar) = parms->st3_amplitude*sin(k*p.pos(2)); - p.rdata(PIdx::f01_Imbar) = 0.0; - p.rdata(PIdx::f11_Rebar) = 0.0; - -#if (NUM_FLAVORS==3) - //just perturbing the electron-muon flavor state, other terms can stay = 0.0 for simplicity - p.rdata(PIdx::f22_Re) = 0.0; - p.rdata(PIdx::f22_Rebar) = 0.0; - p.rdata(PIdx::f02_Re) = 0.0; - p.rdata(PIdx::f02_Im) = 0.0; - p.rdata(PIdx::f12_Re) = 0.0; - p.rdata(PIdx::f12_Im) = 0.0; - p.rdata(PIdx::f02_Rebar) = 0.0; - p.rdata(PIdx::f02_Imbar) = 0.0; - p.rdata(PIdx::f12_Rebar) = 0.0; - p.rdata(PIdx::f12_Imbar) = 0.0; -#endif +//==================================================================================================================// +//========================================= CreateParticlesAtBoundary ==============================================// +//==================================================================================================================// +template +void FlavoredNeutrinoContainer:: +CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt) +{ + BL_PROFILE("FlavoredNeutrinoContainer::CreateParticlesAtBoundary"); - // set energy to 50 MeV to match Richers+(2019) - p.rdata(PIdx::pupt) = 50. * 1e6*CGSUnitsConst::eV; - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); - - // set particle weight such that density is - // 0.5 dm2 c^4 / (2 sqrt(2) GF E) - // to get maximal growth according to Chakraborty 2016 Equation 2.10 - Real dm2 = (parms->mass2-parms->mass1)*(parms->mass2-parms->mass1); //g^2 - Real omega = dm2*PhysConst::c4 / (2.* p.rdata(PIdx::pupt)); - Real mu_ndens = sqrt(2.) * PhysConst::GF; // SI potential divided by the number density - Real ndens = (omega+k*PhysConst::hbarc) / (2.*mu_ndens); // want omega/2mu to be 1 - p.rdata(PIdx::N) = ndens * scale_fac * (1. + u[2]); - p.rdata(PIdx::Nbar) = ndens * scale_fac * (1. - u[2]); - } + const int lev = 0; + const auto dx = Geom(lev).CellSizeArray(); + const auto plo = Geom(lev).ProbLoArray(); + const auto& a_bounds = Geom(lev).ProbDomain(); + + const int nlocs_per_cell = AMREX_D_TERM( parms->nppc[0], + *parms->nppc[1], + *parms->nppc[2]); + + // array of direction vectors + //TODO: We can use a different custom file to set particle data at boundary points. + Gpu::ManagedVector > particle_data = read_particle_data(parms->particle_data_filename);; + auto* particle_data_p = particle_data.dataPtr(); + + // determine the number of directions per location + int ndirs_per_loc = particle_data.size(); + const Real scale_fac = dx[0]*dx[1]*dx[2]/nlocs_per_cell; - //====================// - // 4- k!=0 RANDOMIZED // - //====================// - else if(parms->simulation_type==4){ - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // Set particle flavor - Real rand1, rand2, rand3, rand4; - symmetric_uniform(&rand1); - symmetric_uniform(&rand2); - symmetric_uniform(&rand3); - symmetric_uniform(&rand4); - p.rdata(PIdx::f00_Re) = 1.0; - p.rdata(PIdx::f01_Re) = parms->st4_amplitude*rand1; - p.rdata(PIdx::f01_Im) = parms->st4_amplitude*rand2; - p.rdata(PIdx::f11_Re) = 0.0; - p.rdata(PIdx::f00_Rebar) = 1.0; - p.rdata(PIdx::f01_Rebar) = parms->st4_amplitude*rand3; - p.rdata(PIdx::f01_Imbar) = parms->st4_amplitude*rand4; - p.rdata(PIdx::f11_Rebar) = 0.0; -#if (NUM_FLAVORS==3) - symmetric_uniform(&rand1); - symmetric_uniform(&rand2); - symmetric_uniform(&rand3); - symmetric_uniform(&rand4); - p.rdata(PIdx::f22_Re) = 0.0; - p.rdata(PIdx::f22_Rebar) = 0.0; - p.rdata(PIdx::f02_Re) = parms->st4_amplitude*rand1; - p.rdata(PIdx::f02_Im) = parms->st4_amplitude*rand2; - p.rdata(PIdx::f12_Re) = 0; - p.rdata(PIdx::f12_Im) = 0; - p.rdata(PIdx::f02_Rebar) = parms->st4_amplitude*rand3; - p.rdata(PIdx::f02_Imbar) = parms->st4_amplitude*rand4; - p.rdata(PIdx::f12_Rebar) = 0; - p.rdata(PIdx::f12_Imbar) = 0; + // Loop over multifabs // +#ifdef _OPENMP +#pragma omp parallel #endif + for (MFIter mfi = MakeMFIter(lev); mfi.isValid(); ++mfi) + { + + //Box tilebox () const noexcept: Return the tile Box at the current index. + const Box& tile_box = mfi.tilebox(); + + const int ncellx = parms->ncell[0]; + const int ncelly = parms->ncell[1]; + const int ncellz = parms->ncell[2]; + + //These actually represent the global indices of the tilebox. + const auto lo = amrex::lbound(tile_box); + const auto hi = amrex::ubound(tile_box); - // set energy to 50 MeV to match Richers+(2019) - p.rdata(PIdx::pupt) = 50. * 1e6*CGSUnitsConst::eV; - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); - - // set particle weight such that density is - // 0.5 dm2 c^4 / (2 sqrt(2) GF E) - // to get maximal growth according to Chakraborty 2016 Equation 2.10 - //Real dm2 = (parms->mass2-parms->mass1)*(parms->mass2-parms->mass1); //g^2 - //Real omega = dm2*PhysConst::c4 / (2.* p.rdata(PIdx::pupt)); - //Real mu_ndens = sqrt(2.) * PhysConst::GF; // SI potential divided by the number density - //Real k_expected = (2.*M_PI)/1.0;// corresponding to wavelength of 1cm - //Real ndens_fiducial = (omega+k_expected*PhysConst::hbarc) / (2.*mu_ndens); // want omega/2mu to be 1 - //amrex::Print() << "fiducial ndens would be " << ndens_fiducial << std::endl; + Gpu::ManagedVector counts(tile_box.numPts(), 0); //PODVector > counts(n, 0) + + unsigned int* pcount = counts.dataPtr(); + + Gpu::ManagedVector offsets(tile_box.numPts()); + unsigned int* poffset = offsets.dataPtr(); + + //TODO: This can be used to emit particles not exactly at the boundary, but at n cells away from the boundary (n = buffer). + const int buffer = 0; + + // Determine how many particles to add to the particle tile per cell + //This loop runs over all the particles in a given box. + //For each particle, it calculates a unique "cellid". + //It then adds the pcount for that cell by adding ndirs_per_loc value to it (which is number of particles per location emitted). + //From amrex documentation: Tiling is turned off if GPU is enabled so that more parallelism is exposed to GPU kernels. + //Also note that when tiling is off, tilebox returns validbox. + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept + { + for (int i_part=0; i_partst4_ndens; - Real ndensbar = parms->st4_ndensbar; - Real fhat[3] = {cos(parms->st4_phi) *sin(parms->st4_theta ), - sin(parms->st4_phi) *sin(parms->st4_theta ), - cos(parms->st4_theta )}; - Real fhatbar[3] = {cos(parms->st4_phibar)*sin(parms->st4_thetabar), - sin(parms->st4_phibar)*sin(parms->st4_thetabar), - cos(parms->st4_thetabar)}; - Real costheta = fhat [0]*u[0] + fhat [1]*u[1] + fhat [2]*u[2]; - Real costhetabar = fhatbar[0]*u[0] + fhatbar[1]*u[1] + fhatbar[2]*u[2]; + bool create_particle_this_cell = false; - p.rdata(PIdx::N ) = ndens *scale_fac * (1. + 3.*parms->st4_fluxfac *costheta ); - p.rdata(PIdx::Nbar) = ndensbar*scale_fac * (1. + 3.*parms->st4_fluxfacbar*costhetabar); + //Create particles at outer boundary + switch (DIRECTION) + { + //Create particles in +ve x direction at lower x boundary. + case BoundaryParticleCreationDirection::I_PLUS: + if (i==0+buffer) create_particle_this_cell = true; + break; + + //Create particles in -ve x direction at upper x boundary. + case BoundaryParticleCreationDirection::I_MINUS: + if (i==ncellx-1-buffer) create_particle_this_cell = true; + break; + + //Create particles in +ve y direction at lower y boundary. + case BoundaryParticleCreationDirection::J_PLUS: + if (j==0+buffer) create_particle_this_cell = true; + break; + + //Create particles in -ve y direction at upper y boundary. + case BoundaryParticleCreationDirection::J_MINUS: + if (j==ncelly-1-buffer) create_particle_this_cell = true; + break; + + //Create particles in +ve z direction at lower z boundary. + case BoundaryParticleCreationDirection::K_PLUS: + if (k==0+buffer) create_particle_this_cell = true; + break; + + //Create particles in -ve z direction at upper z boundary. + case BoundaryParticleCreationDirection::K_MINUS: + if (k==ncellz-1-buffer) create_particle_this_cell = true; + break; + + default: + printf("Invalid direction specified. \n"); + assert(0); + break; + } + + if (!create_particle_this_cell) continue; + + int ix = i - lo.x; + int iy = j - lo.y; + int iz = k - lo.z; + int nx = hi.x-lo.x+1; + int ny = hi.y-lo.y+1; + int nz = hi.z-lo.z+1; + unsigned int uix = amrex::min(nx-1,amrex::max(0,ix)); //Forces the value of 'uix' to be in the range of 0 to nx-1. + unsigned int uiy = amrex::min(ny-1,amrex::max(0,iy)); + unsigned int uiz = amrex::min(nz-1,amrex::max(0,iz)); + unsigned int cellid = (uix * ny + uiy) * nz + uiz; + pcount[cellid] += ndirs_per_loc; } + }); + + // Determine total number of particles to add to the particle tile + Gpu::exclusive_scan(counts.begin(), counts.end(), offsets.begin()); //This sets the value of "offsets" - //====================// - // 5- Minerbo Closure // - //====================// - else if(parms->simulation_type==5){ - AMREX_ASSERT(NUM_FLAVORS==3 or NUM_FLAVORS==2); - - // set energy to 50 MeV - p.rdata(PIdx::pupt) = 50. * 1e6*CGSUnitsConst::eV; - p.rdata(PIdx::pupx) = u[0] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupy) = u[1] * p.rdata(PIdx::pupt); - p.rdata(PIdx::pupz) = u[2] * p.rdata(PIdx::pupt); + int num_to_add = offsets[tile_box.numPts()-1] + counts[tile_box.numPts()-1]; + if (num_to_add == 0) continue; + + // this will be the particle ID for the first new particle in the tile + long new_pid; + ParticleType* pstruct; +#ifdef _OPENMP +#pragma omp critical +#endif + { + auto& particles = GetParticles(lev); + auto& particle_tile = particles[std::make_pair(mfi.index(), mfi.LocalTileIndex())]; + + // Resize the particle container + auto old_size = particle_tile.GetArrayOfStructs().size(); + auto new_size = old_size + num_to_add; + particle_tile.resize(new_size); + + for (int i = 0; i< offsets.size(); i++){ + offsets[i] += old_size; + } + + //Returns the next particle ID for this processor. + // Particle IDs start at 1 and are never reused. The pair, consisting of the ID and the CPU on which the particle is "born", is a globally unique identifier for a particle. + //The maximum of this value across all processors must be checkpointed and then restored on restart so that we don't reuse particle IDs. + new_pid = ParticleType::NextID(); + + // set the starting particle ID for the next tile of particles + ParticleType::NextID(new_pid + num_to_add); + + pstruct = particle_tile.GetArrayOfStructs()().data(); + } + + int procID = ParallelDescriptor::MyProc(); + + //===============================================// + // Initialize particle data in the particle tile // + //===============================================// + amrex::ParallelForRNG(tile_box, + [=] AMREX_GPU_DEVICE (int i, int j, int k, amrex::RandomEngine const& engine) noexcept + { + int ix = i - lo.x; + int iy = j - lo.y; + int iz = k - lo.z; + int nx = hi.x-lo.x+1; + int ny = hi.y-lo.y+1; + int nz = hi.z-lo.z+1; + unsigned int uix = amrex::min(nx-1,amrex::max(0,ix)); + unsigned int uiy = amrex::min(ny-1,amrex::max(0,iy)); + unsigned int uiz = amrex::min(nz-1,amrex::max(0,iz)); + unsigned int cellid = (uix * ny + uiy) * nz + uiz; + + for (int i_loc=0; i_locnppc, i_loc); + + Real x, y, z; + + bool create_particle_this_cell = false; + + //Create particles at outer boundary and set face centered coordinates. + //VC=vertex-centered; CC=cell-centered; + switch (DIRECTION) + { + //Create particles in +ve x direction at lower x boundary. + case BoundaryParticleCreationDirection::I_PLUS: + if (i==0+buffer) create_particle_this_cell = true; + x = plo[0]+dx[0]*buffer; //VC, lower x boundary + y = plo[1] + (j + r[1])*dx[1]; //CC + z = plo[2] + (k + r[2])*dx[2]; //CC + break; + + //Create particles in -ve x direction at upper x boundary. + case BoundaryParticleCreationDirection::I_MINUS: + if (i==ncellx-1-buffer) create_particle_this_cell = true; + x = plo[0] + (ncellx-buffer)*dx[0]; //VC, upper x boundary + y = plo[1] + (j + r[1])*dx[1]; //CC + z = plo[2] + (k + r[2])*dx[2]; //CC + break; - // get the cosine of the angle between the direction and each flavor's flux vector - Real mue = fluxfac_e>0 ? (parms->st5_fxnue*u[0] + parms->st5_fynue*u[1] + parms->st5_fznue*u[2])/fluxfac_e : 0; - Real mua = fluxfac_a>0 ? (parms->st5_fxnua*u[0] + parms->st5_fynua*u[1] + parms->st5_fznua*u[2])/fluxfac_a : 0; - Real mux = fluxfac_x>0 ? (parms->st5_fxnux*u[0] + parms->st5_fynux*u[1] + parms->st5_fznux*u[2])/fluxfac_x : 0; - - // get the number of each flavor in this particle. - // parms->st5_nnux contains the number density of mu+tau neutrinos+antineutrinos - // Nnux_thisparticle contains the number of EACH of mu/tau anti/neutrinos (hence the factor of 4) - Real angular_factor; - minerbo_closure(&angular_factor, Ze, mue); - Real Nnue_thisparticle = parms->st5_nnue*scale_fac * angular_factor; - minerbo_closure(&angular_factor, Za, mua); - Real Nnua_thisparticle = parms->st5_nnua*scale_fac * angular_factor; - minerbo_closure(&angular_factor, Zx, mux); - Real Nnux_thisparticle = parms->st5_nnux*scale_fac * angular_factor / 4.0; - - // set total number of neutrinos the particle has as the sum of the flavors - p.rdata(PIdx::N ) = Nnue_thisparticle + Nnux_thisparticle; - p.rdata(PIdx::Nbar) = Nnua_thisparticle + Nnux_thisparticle; + //Create particles in +ve y direction at lower y boundary. + case BoundaryParticleCreationDirection::J_PLUS: + if (j==0+buffer) create_particle_this_cell = true; + y = plo[1]+dx[1]*buffer; //VC, lower y boundary + x = plo[0] + (i + r[0])*dx[0]; //CC + z = plo[2] + (k + r[2])*dx[2]; //CC + break; + + //Create particles in -ve y direction at upper y boundary. + case BoundaryParticleCreationDirection::J_MINUS: + if (j==ncelly-1-buffer) create_particle_this_cell = true; + y = plo[1] + (ncelly-buffer)*dx[1]; //VC, upper y boundary + x = plo[0] + (i + r[0])*dx[0]; //CC + z = plo[2] + (k + r[2])*dx[2]; //CC + break; + + //Create particles in +ve z direction at lower z boundary. + case BoundaryParticleCreationDirection::K_PLUS: + if (k==0+buffer) create_particle_this_cell = true; + z = plo[2]+dx[2]*buffer; //VC, lower z boundary + x = plo[0] + (i + r[0])*dx[0]; //CC + y = plo[1] + (j + r[1])*dx[1]; //CC + break; + + //Create particles in -ve z direction at upper z boundary. + case BoundaryParticleCreationDirection::K_MINUS: + if (k==ncellz-1-buffer) create_particle_this_cell = true; + z = plo[2] + (ncellz-buffer)*dx[2]; //VC, upper z boundary + x = plo[0] + (i + r[0])*dx[0]; //CC + y = plo[1] + (j + r[1])*dx[1]; //CC + break; + + default: + printf("Invalid direction specified. \n"); + assert(0); + break; + } + + if (!create_particle_this_cell) continue; + + for(int i_direction=0; i_direction= 0); + AMREX_ASSERT(p.rdata(PIdx::N11_Re ) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N00_Rebar) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N11_Rebar) >= 0); #if NUM_FLAVORS==3 - p.rdata(PIdx::N ) += Nnux_thisparticle; - p.rdata(PIdx::Nbar) += Nnux_thisparticle; + AMREX_ASSERT(p.rdata(PIdx::N22_Re ) >= 0); + AMREX_ASSERT(p.rdata(PIdx::N22_Rebar) >= 0); #endif - // set on-diagonals to have relative proportion of each flavor - p.rdata(PIdx::f00_Re) = Nnue_thisparticle / p.rdata(PIdx::N ); - p.rdata(PIdx::f11_Re) = Nnux_thisparticle / p.rdata(PIdx::N ); - p.rdata(PIdx::f00_Rebar) = Nnua_thisparticle / p.rdata(PIdx::Nbar); - p.rdata(PIdx::f11_Rebar) = Nnux_thisparticle / p.rdata(PIdx::Nbar); + // Set particle position + p.pos(0) = x; + p.pos(1) = y; + p.pos(2) = z; + + // Set particle integrated position + p.rdata(PIdx::x) = x; + p.rdata(PIdx::y) = y; + p.rdata(PIdx::z) = z; + p.rdata(PIdx::time) = 0; + + // scale particle numbers based on number of points per cell and the cell volume + p.rdata(PIdx::N00_Re ) *= scale_fac; + p.rdata(PIdx::N11_Re ) *= scale_fac; + p.rdata(PIdx::N00_Rebar) *= scale_fac; + p.rdata(PIdx::N11_Rebar) *= scale_fac; #if NUM_FLAVORS==3 - p.rdata(PIdx::f22_Re) = Nnux_thisparticle / p.rdata(PIdx::N ); - p.rdata(PIdx::f22_Rebar) = Nnux_thisparticle / p.rdata(PIdx::Nbar); + p.rdata(PIdx::N22_Re ) *= scale_fac; + p.rdata(PIdx::N22_Rebar) *= scale_fac; #endif - // random perturbations to the off-diagonals - Real rand; - symmetric_uniform(&rand); - p.rdata(PIdx::f01_Re) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Re ) - p.rdata(PIdx::f11_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f01_Im) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Re ) - p.rdata(PIdx::f11_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f01_Rebar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Rebar) - p.rdata(PIdx::f11_Rebar)); - symmetric_uniform(&rand); - p.rdata(PIdx::f01_Imbar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Rebar) - p.rdata(PIdx::f11_Rebar)); + if(parms->IMFP_method == 1){ + const Real V_momentum = 4*MathConst::pi*(pow(p.rdata(PIdx::pupt)+parms->delta_E/2,3)-pow(p.rdata(PIdx::pupt)-parms->delta_E/2,3))/(3*ndirs_per_loc*parms->nppc[0]*parms->nppc[1]*parms->nppc[2]); + + //p.rdata(PIdx::Vphase) = dx[0]*dx[1]*dx[2]*V_momentum; + const Real dt = current_dt; + const Real clight = PhysConst::c; + const Real pupx_ = p.rdata(PIdx::pupx); + const Real pupy_ = p.rdata(PIdx::pupy); + const Real pupz_ = p.rdata(PIdx::pupz); + const Real pupt_ = p.rdata(PIdx::pupt); + + switch (DIRECTION) + { + //Create particles in +ve x direction at lower x boundary. + case BoundaryParticleCreationDirection::I_PLUS: + p.rdata(PIdx::Vphase) = dx[1]*dx[2]*clight*dt*V_momentum*std::abs(pupx_/pupt_); + break; + + //Create particles in -ve x direction at upper x boundary. + case BoundaryParticleCreationDirection::I_MINUS: + p.rdata(PIdx::Vphase) = dx[1]*dx[2]*clight*dt*V_momentum*std::abs(pupx_/pupt_); + break; + + //Create particles in +ve y direction at lower y boundary. + case BoundaryParticleCreationDirection::J_PLUS: + p.rdata(PIdx::Vphase) = dx[0]*dx[2]*clight*dt*V_momentum*std::abs(pupy_/pupt_); + break; + + //Create particles in -ve y direction at upper y boundary. + case BoundaryParticleCreationDirection::J_MINUS: + p.rdata(PIdx::Vphase) = dx[0]*dx[2]*clight*dt*V_momentum*std::abs(pupy_/pupt_); + break; + + //Create particles in +ve z direction at lower z boundary. + case BoundaryParticleCreationDirection::K_PLUS: + p.rdata(PIdx::Vphase) = dx[0]*dx[1]*clight*dt*V_momentum*std::abs(pupz_/pupt_); + break; + + //Create particles in -ve z direction at upper z boundary. + case BoundaryParticleCreationDirection::K_MINUS: + p.rdata(PIdx::Vphase) = dx[0]*dx[1]*clight*dt*V_momentum*std::abs(pupz_/pupt_); + break; + + default: + printf("Invalid direction specified. \n"); + assert(0); + break; + } + } + + //Set off-diagonal terms to zero //TODO: This should be reviewed once. + p.rdata(PIdx::N01_Re) = 0.0; + p.rdata(PIdx::N01_Im) = 0.0; + p.rdata(PIdx::N01_Rebar) = 0.0; + p.rdata(PIdx::N01_Imbar) = 0.0; #if NUM_FLAVORS==3 - symmetric_uniform(&rand); - p.rdata(PIdx::f02_Re) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Re ) - p.rdata(PIdx::f22_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f02_Im) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Re ) - p.rdata(PIdx::f22_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f12_Re) = parms->st5_amplitude*rand * (p.rdata(PIdx::f11_Re ) - p.rdata(PIdx::f22_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f12_Im) = parms->st5_amplitude*rand * (p.rdata(PIdx::f11_Re ) - p.rdata(PIdx::f22_Re )); - symmetric_uniform(&rand); - p.rdata(PIdx::f02_Rebar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Rebar) - p.rdata(PIdx::f22_Rebar)); - symmetric_uniform(&rand); - p.rdata(PIdx::f02_Imbar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f00_Rebar) - p.rdata(PIdx::f22_Rebar)); - symmetric_uniform(&rand); - p.rdata(PIdx::f12_Rebar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f11_Rebar) - p.rdata(PIdx::f22_Rebar)); - symmetric_uniform(&rand); - p.rdata(PIdx::f12_Imbar) = parms->st5_amplitude*rand * (p.rdata(PIdx::f11_Rebar) - p.rdata(PIdx::f22_Rebar)); + p.rdata(PIdx::N02_Re) = 0.0; + p.rdata(PIdx::N02_Im) = 0.0; + p.rdata(PIdx::N12_Re) = 0.0; + p.rdata(PIdx::N12_Im) = 0.0; + p.rdata(PIdx::N02_Rebar) = 0.0; + p.rdata(PIdx::N02_Imbar) = 0.0; + p.rdata(PIdx::N12_Rebar) = 0.0; + p.rdata(PIdx::N12_Imbar) = 0.0; #endif - } - else{ - amrex::Error("Invalid simulation type"); - } + } // loop over direction + } // loop over location + }); // loop over grid cells + + } // loop over multifabs + +} // CreateParticlesAtBoundary() + +//We need to explicitly instantiate the template function for different use cases. +//DIRECTION == BoundaryParticleCreationDirection::I_PLUS (+ve x direction at lower x boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); +//DIRECTION == BoundaryParticleCreationDirection::I_MINUS (-ve x direction at upper x boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); +//DIRECTION == BoundaryParticleCreationDirection::J_PLUS (+ve y direction at lower y boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); +//DIRECTION == BoundaryParticleCreationDirection::J_MINUS (-ve y direction at upper y boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); +//DIRECTION == BoundaryParticleCreationDirection::K_PLUS (+ve z direction at lower z boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); +//DIRECTION == BoundaryParticleCreationDirection::K_MINUS (-ve z direction at upper z boundary.) +template void FlavoredNeutrinoContainer::CreateParticlesAtBoundary(const TestParams* parms, const Real current_dt); - #include "generated_files/FlavoredNeutrinoContainerInit.cpp_set_trace_length" - } - } - }); - } - // get the minimum neutrino energy for calculating the timestep - Real pupt_min = amrex::ReduceMin(*this, [=] AMREX_GPU_DEVICE (const FlavoredNeutrinoContainer::ParticleType& p) -> Real { return p.rdata(PIdx::pupt); }); - ParallelDescriptor::ReduceRealMin(pupt_min); - #include "generated_files/FlavoredNeutrinoContainerInit.cpp_Vvac_fill" -} diff --git a/Source/IO.H b/Source/IO.H index e851f0a2..c159b065 100644 --- a/Source/IO.H +++ b/Source/IO.H @@ -6,6 +6,15 @@ #include #include +void +InitializeReducedData0D(); + +void +WriteReducedData0D(const amrex::MultiFab& state, + const FlavoredNeutrinoContainer& neutrinos, + const amrex::Geometry& geom, amrex::Real time, + int step); + void WritePlotFile (const amrex::MultiFab& state, const FlavoredNeutrinoContainer& neutrinos, diff --git a/Source/IO.cpp b/Source/IO.cpp index a1b09cea..2a839ef8 100644 --- a/Source/IO.cpp +++ b/Source/IO.cpp @@ -2,6 +2,14 @@ #include #include +//We use the AMReX binary format to write data for now +//That's because the HDF5 write format gives errors when running with CUDA. +#undef AMREX_USE_HDF5 + +#ifdef AMREX_USE_HDF5 +#include "hdf5.h" +#endif + #include "FlavoredNeutrinoContainer.H" #include "IO.H" #include "Evolve.H" @@ -19,18 +27,25 @@ WritePlotFile (const amrex::MultiFab& state, BoxArray grids = state.boxArray(); grids.convert(IntVect()); - const DistributionMapping& dmap = state.DistributionMap(); - const std::string& plotfilename = amrex::Concatenate("plt", step); amrex::Print() << " Writing plotfile " << plotfilename << "\n"; + // write the grid file +#ifdef AMREX_USE_HDF5 + amrex::WriteSingleLevelPlotfileHDF5(plotfilename, state, GIdx::names, geom, time, step); +#else amrex::WriteSingleLevelPlotfile(plotfilename, state, GIdx::names, geom, time, step); +#endif if (write_plot_particles == 1) { auto neutrino_varnames = neutrinos.get_attribute_names(); +#ifdef AMREX_USE_HDF5 + neutrinos.CheckpointHDF5(plotfilename, "neutrinos", true, neutrino_varnames); +#else neutrinos.Checkpoint(plotfilename, "neutrinos", true, neutrino_varnames); +#endif } // write job information @@ -44,6 +59,31 @@ RecoverParticles (const std::string& dir, { BL_PROFILE("RecoverParticles()"); +#ifdef AMREX_USE_HDF5 + // open the plotfile to get the metadata + std::string plotfilename = dir; + plotfilename.append(".h5"); + hid_t file = H5Fopen(plotfilename.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT); + hid_t group = H5Gopen(file, "level_0", H5P_DEFAULT); + + // read the attributes to get step number + hid_t attr = H5Aopen(group, "steps", H5P_DEFAULT); + H5Aread(attr, H5T_NATIVE_INT, &step); + H5Aclose(attr); + + // read the attributes to get the current time + attr = H5Aopen(group, "time", H5P_DEFAULT); + H5Aread(attr, H5T_NATIVE_DOUBLE, &time); + H5Aclose(attr); + + // close all the pointers + H5Fclose(file); + H5Gclose(group); + + // initialize our particle container from the plotfile + std::string checkpointfilename("neutrinos/neutrinos"); + neutrinos.RestartHDF5(dir, checkpointfilename); +#else // load the metadata from this plotfile PlotFileData plotfile(dir); @@ -57,6 +97,7 @@ RecoverParticles (const std::string& dir, // initialize our particle container from the plotfile std::string file("neutrinos"); neutrinos.Restart(dir, file); +#endif // print the step/time for the restart amrex::Print() << "Restarting after time step: " << step-1 << " t = " << time << " s. ct = " << PhysConst::c * time << " cm" << std::endl; diff --git a/Source/Make.package b/Source/Make.package index 191e7a7f..63f5b4e4 100644 --- a/Source/Make.package +++ b/Source/Make.package @@ -1,8 +1,13 @@ CEXE_sources += main.cpp +CEXE_sources += DataReducer.cpp CEXE_sources += IO.cpp CEXE_sources += FlavoredNeutrinoContainerInit.cpp CEXE_sources += Evolve.cpp CEXE_sources += FlavoredNeutrinoContainer.cpp +CEXE_sources += ReadEosTable.cpp +CEXE_sources += ReadNuLibTable.cpp +CEXE_sources += ReadInput_RhoTempYe.cpp +CEXE_sources += ReadHDF5RhoYeT.cpp CEXE_headers += Evolve.H CEXE_headers += FlavoredNeutrinoContainer.H @@ -11,3 +16,13 @@ CEXE_headers += Particles.H CEXE_headers += IO.H CEXE_headers += Parameters.H CEXE_headers += ParticleInterpolator.H +CEXE_headers += DataReducer.H +CEXE_headers += ArithmeticArray.H +CEXE_headers += EosTable.H +CEXE_headers += EosTableFunctions.H +CEXE_headers += EosTableHelpers.H +CEXE_headers += NuLibTable.H +CEXE_headers += NuLibTableFunctions.H +CEXE_headers += NuLibTableHelpers.H +CEXE_headers += ReadInput_RhoTempYe.H +CEXE_headers += ReadHdf5RhoYeT.H diff --git a/Source/NuLibTable.H b/Source/NuLibTable.H new file mode 100644 index 00000000..262460ed --- /dev/null +++ b/Source/NuLibTable.H @@ -0,0 +1,37 @@ +#ifndef NULIB_TABLE_H +#define NULIB_TABLE_H + +void ReadNuLibTable(const std::string nulib_table_name); + +#define NTABLES_NULIB 2 + +// table key +// 0 absoprtion_opacity +// 1 scattering opacity + + +namespace nulib_private { + +// table data + extern double *alltables_nulib; + //extern double *epstable; + extern double *logrho_nulib; + extern double *logtemp_nulib; + extern double *yes_nulib; + extern double *species_nulib; + extern double *group_nulib; + +#define NULIBVAR(VAR) helperVarsReal_nulib[helperVarsEnumReal_nulib::VAR] +//WARNING: If the number of variables is increased here, then memory allocation for helperVarsReal/helperVarsInt pointer in GRHydroX_ReadEOSTable.cxx will also need to be increased. + enum helperVarsEnumReal_nulib { eos_rhomin, eos_rhomax, eos_tempmin, eos_tempmax, eos_yemin, eos_yemax, + dlintemp, dlintempi, drholintempi, dlintempyei, drholintempyei, + dtemp, dtempi, drho, drhoi, dye, dyei, drhotempi, drhoyei, dtempyei, + drhotempyei, eos_epsmin, eos_epsmax, idx_group}; + extern double *helperVarsReal_nulib; + +#define NULIBVAR_INT(VAR) helperVarsInt_nulib[helperVarsEnumInt_nulib::VAR] + enum helperVarsEnumInt_nulib { nrho, ntemp, nye, nspecies, ngroup } ; + extern int *helperVarsInt_nulib; +} + +#endif // NULIB_TABLE_H diff --git a/Source/NuLibTableFunctions.H b/Source/NuLibTableFunctions.H new file mode 100644 index 00000000..95f2c198 --- /dev/null +++ b/Source/NuLibTableFunctions.H @@ -0,0 +1,44 @@ +#ifndef NULIBTABLEFUCNTIONS_HXX +#define NULIBTABLEFUCNTIONS_HXX + + +#include "NuLibTableHelpers.H" + +struct NuLib_tabulated { + double *alltables_nulib, *logrho_nulib, *logtemp_nulib, *yes_nulib, *helperVarsReal_nulib; + int *helperVarsInt_nulib; + + //constructor for NuLib_tabulated + AMREX_GPU_DEVICE AMREX_GPU_HOST NuLib_tabulated() = default;//Need to keep it + AMREX_GPU_DEVICE AMREX_GPU_HOST NuLib_tabulated(double *alltables_nulib, double *logrho_nulib, + double *logtemp_nulib, double *yes_nulib, double *helperVarsReal_nulib, int *helperVarsInt_nulib): + alltables_nulib(alltables_nulib), logrho_nulib(logrho_nulib), logtemp_nulib(logtemp_nulib), + yes_nulib(yes_nulib), helperVarsReal_nulib(helperVarsReal_nulib), helperVarsInt_nulib(helperVarsInt_nulib) {} + + //--------------- get helperVarsReal pointer --------------------------------------------- + AMREX_GPU_DEVICE AMREX_GPU_HOST double *get_helperVarsReal_nulib() const { + return helperVarsReal_nulib; + };//get_helperVarsReal_nulib + + + //--------------- get opacaties --------------------------------------------- + AMREX_GPU_DEVICE AMREX_GPU_HOST void get_opacities(double rho, double temperature, double Ye, + double &absorption_opacity, double &scattering_opacity, + int &keyerr, int &anyerr, const int idx_species, const int idx_group) const { + + nulib_opacities(rho, temperature, Ye, absorption_opacity, scattering_opacity, keyerr, anyerr, + alltables_nulib, logrho_nulib, logtemp_nulib, yes_nulib, helperVarsReal_nulib, helperVarsInt_nulib, + idx_species, idx_group); + return; + + /*Actual steps: + (1) check bounds + (2) get interp spots + (3) do interpolation to get absorption and scattering opacity + */ + };//get_opacities + + +}; //struct NuLib_tabulated + +#endif // NULIBTABLEFUCNTIONS_HXX \ No newline at end of file diff --git a/Source/NuLibTableHelpers.H b/Source/NuLibTableHelpers.H new file mode 100644 index 00000000..e40ae532 --- /dev/null +++ b/Source/NuLibTableHelpers.H @@ -0,0 +1,183 @@ +#ifndef NULIBTABLEHELPERS_HXX +#define NULIBTABLEHELPERS_HXX + +#include "NuLibTable.H" + +//Check whether input (rho, temperature, Ye) are within the table bounds. +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST int +checkbounds_nulib(const double xrho, const double xtemp, const double xye, double *helperVarsReal_nulib) { + + using namespace nulib_private; + // keyerr codes: + // 101 -- Y_e too high + // 102 -- Y_e too low + // 103 -- temp too high (if keytemp = 1) + // 104 -- temp too low (if keytemp = 1) + // 105 -- rho too high + // 106 -- rho too low + + if(xrho > NULIBVAR(eos_rhomax)) { + return 105; + } + if(xrho < NULIBVAR(eos_rhomin)) { + return 106; + } + if(xye > NULIBVAR(eos_yemax)) { + return 101; + } + if(xye < NULIBVAR(eos_yemin)) { + return 102; + } + if(xtemp > NULIBVAR(eos_tempmax)) { + return 103; + } + if(xtemp < NULIBVAR(eos_tempmin)) { + return 104; + } + return 0; + +}//function checkbounds_nulib + +//Get the indices to prepare for interpolation. +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +get_interp_spots_nulib(const double x, const double y, const double z, double &delx, + double &dely, double &delz, int *idx, double *logrho_nulib, + double *logtemp_nulib, double *yes_nulib, double *helperVarsReal_nulib, int *helperVarsInt_nulib, + const int idx_species, const int idx_group) +{ + using namespace nulib_private; + + int ix = 1 + (int)( (x - logrho_nulib[0] - 1.0e-10) * NULIBVAR(drhoi) ); + int iy = 1 + (int)( (y - logtemp_nulib[0] - 1.0e-10) * NULIBVAR(dtempi) ); + int iz = 1 + (int)( (z - yes_nulib[0] - 1.0e-10) * NULIBVAR(dyei) ); + + const int nrho = NULIBVAR_INT(nrho); + const int ntemp = NULIBVAR_INT(ntemp); + const int nye = NULIBVAR_INT(nye); + const int nspecies = NULIBVAR_INT(nspecies); + + ix = MAX2( 1, MIN2( ix, nrho-1 ) ); + iy = MAX2( 1, MIN2( iy, ntemp-1 ) ); + iz = MAX2( 1, MIN2( iz, nye-1 ) ); + + //int indnew = iv + NTABLES_NULIB*(i + nrho_*(j + ntemp_*k)); //indexing in EoS table + //int indnew = iv + NTABLES_NULIB*(i + nrho_*(j + ntemp_*(k + nye_*(l + nspecies_*m)))); //indexing in NuLib table + + idx[0] = NTABLES_NULIB*(ix + nrho*(iy + ntemp*(iz + nye*(idx_species + nspecies*idx_group)))); + idx[1] = NTABLES_NULIB*((ix-1) + nrho*(iy + ntemp*(iz + nye*(idx_species + nspecies*idx_group)))); + idx[2] = NTABLES_NULIB*(ix + nrho*((iy-1) + ntemp*(iz + nye*(idx_species + nspecies*idx_group)))); + idx[3] = NTABLES_NULIB*(ix + nrho*(iy + ntemp*((iz-1) + nye*(idx_species + nspecies*idx_group)))); + idx[4] = NTABLES_NULIB*((ix-1) + nrho*((iy-1) + ntemp*(iz + nye*(idx_species + nspecies*idx_group)))); + idx[5] = NTABLES_NULIB*((ix-1) + nrho*(iy + ntemp*((iz-1) + nye*(idx_species + nspecies*idx_group)))); + idx[6] = NTABLES_NULIB*(ix + nrho*((iy-1) + ntemp*((iz-1) + nye*(idx_species + nspecies*idx_group)))); + idx[7] = NTABLES_NULIB*((ix-1) + nrho*((iy-1) + ntemp*((iz-1) + nye*(idx_species + nspecies*idx_group)))); + + + // set up aux vars for interpolation + delx = logrho_nulib[ix] - x; + dely = logtemp_nulib[iy] - y; + delz = yes_nulib[iz] - z; + + return; +} // function get_interp_spots_nulib + +//Perform the interpolation +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +nuc_eos_C_linterp_one_nulib(const int *idx, const double delx, const double dely, + const double delz, double &f, const int iv, + double *alltables_nulib, double *helperVarsReal_nulib) +{ + using namespace nulib_private; + + // helper variables + double fh[8], a[8]; + + fh[0] = alltables_nulib[iv+idx[0]]; + fh[1] = alltables_nulib[iv+idx[1]]; + fh[2] = alltables_nulib[iv+idx[2]]; + fh[3] = alltables_nulib[iv+idx[3]]; + fh[4] = alltables_nulib[iv+idx[4]]; + fh[5] = alltables_nulib[iv+idx[5]]; + fh[6] = alltables_nulib[iv+idx[6]]; + fh[7] = alltables_nulib[iv+idx[7]]; + + // set up coeffs of interpolation polynomical and + // evaluate function values + a[0] = fh[0]; + a[1] = NULIBVAR(drhoi) * ( fh[1] - fh[0] ); + a[2] = NULIBVAR(dtempi) * ( fh[2] - fh[0] ); + a[3] = NULIBVAR(dyei) * ( fh[3] - fh[0] ); + a[4] = NULIBVAR(drhotempi) * ( fh[4] - fh[1] - fh[2] + fh[0] ); + a[5] = NULIBVAR(drhoyei) * ( fh[5] - fh[1] - fh[3] + fh[0] ); + a[6] = NULIBVAR(dtempyei) * ( fh[6] - fh[2] - fh[3] + fh[0] ); + a[7] = NULIBVAR(drhotempyei) * ( fh[7] - fh[0] + fh[1] + fh[2] + + fh[3] - fh[4] - fh[5] - fh[6] ); + + f = a[0] + a[1] * delx + + a[2] * dely + + a[3] * delz + + a[4] * delx * dely + + a[5] * delx * delz + + a[6] * dely * delz + + a[7] * delx * dely * delz; + + return; +} // function nuc_eos_C_linterp_one_nulib + + + +//Main function for absorption and scattering opacities (rho, temperature, Ye) +static inline AMREX_GPU_DEVICE AMREX_GPU_HOST void +nulib_opacities(const double rho, const double temperature, + const double Ye, double &absorption_opacity, double &scattering_opacity, + int &keyerr, int &anyerr, double *alltables_nulib, + double *logrho_nulib, double *logtemp_nulib, double *yes_nulib, + double *helperVarsReal_nulib, int *helperVarsInt_nulib, + const int idx_species, const int idx_group) +{ + using namespace nulib_private; + + int anyerr_ = 0; + // check if we are fine + int keyerr_ = checkbounds_nulib(rho, temperature, Ye, helperVarsReal_nulib); + if(keyerr_ != 0) anyerr_ = 1; + + keyerr = keyerr_ ; anyerr = anyerr_; + // Abort if there is any error in checkbounds. + // This should never happen and the program should abort with + // a fatal error anyway. No point in doing any further EOS calculations. + if(anyerr_){ + printf("Error in checkbounds::%s::%d::%s, keyerr = %d\n", __FILE__, __LINE__, __func__, keyerr); + printf(" rho = %.5e, temperature = %.5e, Ye = %.6f \n", rho, temperature, Ye); + return; + } + + int idx[8]; + double delx,dely,delz; + const double lrho = log(rho); + const double ltemp = log(temperature); + + get_interp_spots_nulib(lrho, ltemp, Ye, delx, dely, delz, idx, + logrho_nulib, logtemp_nulib, yes_nulib, helperVarsReal_nulib, helperVarsInt_nulib, + idx_species, idx_group); + + double absorption_opacity_ , scattering_opacity_; + { + const int iv = 0; + nuc_eos_C_linterp_one_nulib(idx, delx, dely, delz, absorption_opacity_, iv, alltables_nulib, helperVarsReal_nulib); + } + + { + const int iv = 1; + nuc_eos_C_linterp_one_nulib(idx, delx, dely, delz, scattering_opacity_, iv, alltables_nulib, helperVarsReal_nulib); + } + + // Assign values to reference variables: + absorption_opacity = absorption_opacity_; + scattering_opacity = scattering_opacity_; + + return; +}//nulib_opacities + + +#endif //NULIBTABLEHELPERS_HXX \ No newline at end of file diff --git a/Source/Parameters.H b/Source/Parameters.H index 498b6d91..c7ff098a 100644 --- a/Source/Parameters.H +++ b/Source/Parameters.H @@ -8,64 +8,86 @@ using namespace amrex; + +//=========================================// +// Structure of global parameter variables // +//=========================================// struct TestParams : public amrex::Gpu::Managed { - IntVect ncell; // num cells in domain - IntVect nppc; // number of particles per cell in each dim - int nphi_equator; // number of directions in x-y plane. - Real Lx, Ly, Lz; - int max_grid_size; - int nsteps; - Real end_time; - int write_plot_every; - int write_plot_particles_every; - Real rho_in, Ye_in, T_in; // g/ccm, 1, MeV - int simulation_type; - Real cfl_factor, flavor_cfl_factor; - Real max_adaptive_speedup; - bool do_restart; - std::string restart_dir; + IntVect ncell; // Number of cells in the domain + IntVect nppc; // Number of points of particle emission per cell in each dimension + Real Lx, Ly, Lz; // Length of the domain in each dimension + int max_grid_size; // Maximum grid size subdivisions of the domain for parallelization + int nsteps; // Number of time steps + Real end_time; // End time of the simulation + int write_plot_every; // Write plot files every n steps + int write_plot_particles_every; // Write plot files for particles every n steps + Real rho_in; // Density in g/ccm + Real Ye_in; // Electron fraction (dimensionless quantity) + Real kT_in; // Temperature in erg + Real cfl_factor, flavor_cfl_factor, collision_cfl_factor; + Real max_adaptive_speedup; + bool do_restart; // Flag to restart from a previous simulation + std::string restart_dir; // Directory path to restart from in case do_restart is true Real maxError; - // neutrino physics parameters. See first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf - Real mass1, mass2, mass3; // neutrino masses in grams - Real theta12, theta13, theta23; // neutrino mixing angles in radians - Real alpha1, alpha2; // Majorana phases, radians - Real deltaCP; // CP violating phases in radians - - // simulation_type==3 - int st3_wavelength_fraction_of_domain; - Real st3_amplitude; - - // simulation_type==4 - Real st4_ndens , st4_theta , st4_phi , st4_fluxfac ; - Real st4_ndensbar, st4_thetabar, st4_phibar, st4_fluxfacbar; - Real st4_amplitude; - - // simulation_type==5 - Real st5_nnue , st5_nnua , st5_nnux ; - Real st5_fxnue, st5_fxnua, st5_fxnux; - Real st5_fynue, st5_fynua, st5_fynux; - Real st5_fznue, st5_fznua, st5_fznux; - Real st5_amplitude; + // File name with particles' initial conditions + // This includes energy, momentum, N, Nbar, phase space volume, and others. + std::string particle_data_filename; + + // Neutrino physics parameters. See the first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf + Real mass1, mass2, mass3; // Neutrino masses in grams + Real theta12, theta13, theta23; // Neutrino mixing angles in radians + Real alpha1, alpha2; // Majorana phases in radians + Real deltaCP; // CP-violating phases in radians + + // perturbation parameters + int perturbation_type; + Real perturbation_wavelength_cm; + Real perturbation_amplitude; + + // Absorption opacities and equilibrium neutrino chemical potentials + int IMFP_method; // Flag to choose the method to calculate the opacities + int Do_Pauli_blocking; // If 1, it will multiply the opacities by 1 / (1 - f_eq); if 0, do nothing. + Real IMFP_abs[2][NUM_FLAVORS]; + Real IMFP_scat[2][NUM_FLAVORS]; + Real munu[2][NUM_FLAVORS]; // equilibrium electron neutrino chemical potential in erg (CGS unist) + Real delta_E; // erg (CGS unist) + + // Attenuation to Hamiltonians + Real attenuation_hamiltonians; // Multiplication factor to [N,H] + + // Black hole properties + int do_blackhole; // Flag to include a black hole in the simulation + int do_periodic_empty_bc; // Flag to include a periodic empty boundary condition + double bh_radius; // Black hole radius (cm) + double bh_center_x; // Black hole x coordinate (cm) + double bh_center_y; // Black hole y coordinate (cm) + double bh_center_z; // Black hole z coordinate (cm) + + // Background matter rho, Ye, T flag + int read_rho_T_Ye_from_table; // Flag to read rho, Ye, T from a table + + // HDF5 table names (with full path) for EoS and NuLib tables + std::string nuceos_table_name; // EoS table file name + std::string nulib_table_name; // NuLib table file name + std::string background_rho_Ye_T_table_name; // Background matter table file name + int boundary_condition_type; // Boundary condition type flag void Initialize(){ + ParmParse pp; - pp.get("simulation_type", simulation_type); pp.get("ncell", ncell); pp.get("Lx", Lx); pp.get("Ly", Ly); pp.get("Lz", Lz); pp.get("nppc", nppc); - pp.get("nphi_equator", nphi_equator); pp.get("max_grid_size", max_grid_size); pp.get("nsteps", nsteps); pp.get("end_time", end_time); - pp.get("rho_g_ccm", rho_in); - pp.get("Ye", Ye_in); - pp.get("T_MeV", T_in); pp.get("cfl_factor", cfl_factor); pp.get("flavor_cfl_factor", flavor_cfl_factor); + pp.get("collision_cfl_factor", collision_cfl_factor); pp.get("max_adaptive_speedup", max_adaptive_speedup); pp.get("write_plot_every", write_plot_every); pp.get("write_plot_particles_every", write_plot_particles_every); @@ -73,6 +95,16 @@ struct TestParams : public amrex::Gpu::Managed pp.get("restart_dir", restart_dir); pp.get("maxError", maxError); + // File name with particles' initial conditions + // This includes energy, momentum, N, Nbar, phase space volume, and others. + pp.get("particle_data_filename",particle_data_filename); + + // perturbation parameters + pp.get("perturbation_amplitude", perturbation_amplitude); + pp.get("perturbation_type", perturbation_type); + if(perturbation_type == 1) + pp.get("perturbation_wavelength_cm", perturbation_wavelength_cm); + // neutrino physics parameters for 2-flavor pp.get("mass1_eV", mass1); pp.get("mass2_eV", mass2); @@ -82,53 +114,90 @@ struct TestParams : public amrex::Gpu::Managed mass2 *= CGSUnitsConst::eV/PhysConst::c2; theta12 *= M_PI/180.; alpha1 *= M_PI/180.; - if(NUM_FLAVORS>=2){ - pp.get("mass3_eV", mass3); - pp.get("theta13_degrees", theta13); - pp.get("theta23_degrees", theta23); - pp.get("alpha2_degrees", alpha2); - pp.get("deltaCP_degrees", deltaCP); - mass3 *= CGSUnitsConst::eV/PhysConst::c2; - theta13 *= M_PI/180.; - theta23 *= M_PI/180.; - alpha2 *= M_PI/180.; - deltaCP *= M_PI/180.; + pp.get("mass3_eV", mass3); + pp.get("theta13_degrees", theta13); + pp.get("theta23_degrees", theta23); + pp.get("alpha2_degrees", alpha2); + pp.get("deltaCP_degrees", deltaCP); + mass3 *= CGSUnitsConst::eV/PhysConst::c2; + theta13 *= M_PI/180.; + theta23 *= M_PI/180.; + alpha2 *= M_PI/180.; + deltaCP *= M_PI/180.; } - if(simulation_type==3){ - pp.get("st3_amplitude", st3_amplitude); - pp.get("st3_wavelength_fraction_of_domain", st3_wavelength_fraction_of_domain); - } - - if(simulation_type==4){ - pp.get("st4_theta" , st4_theta ); - pp.get("st4_thetabar", st4_thetabar); - pp.get("st4_phi" , st4_phi); - pp.get("st4_phibar", st4_phibar); - pp.get("st4_ndens" , st4_ndens); - pp.get("st4_ndensbar", st4_ndensbar); - pp.get("st4_fluxfac" , st4_fluxfac); - pp.get("st4_fluxfacbar", st4_fluxfacbar); - pp.get("st4_amplitude", st4_amplitude); - } - - if(simulation_type==5){ - pp.get("st5_nnue",st5_nnue); - pp.get("st5_nnua",st5_nnua); - pp.get("st5_nnux",st5_nnux); - pp.get("st5_fxnue",st5_fxnue); - pp.get("st5_fxnua",st5_fxnua); - pp.get("st5_fxnux",st5_fxnux); - pp.get("st5_fynua",st5_fynua); - pp.get("st5_fynux",st5_fynux); - pp.get("st5_fynue",st5_fynue); - pp.get("st5_fznue",st5_fznue); - pp.get("st5_fznua",st5_fznua); - pp.get("st5_fznux",st5_fznux); - pp.get("st5_amplitude",st5_amplitude); - } + // attenuation to hamiltonians + pp.get("attenuation_hamiltonians", attenuation_hamiltonians); + + // Boundary condition type + pp.get("boundary_condition_type", boundary_condition_type); + pp.get("do_periodic_empty_bc", do_periodic_empty_bc); + + // Background matter flag + pp.get("read_rho_T_Ye_from_table", read_rho_T_Ye_from_table); + // Background matter options + if(read_rho_T_Ye_from_table==0){ + // Set constant matter field + pp.get("rho_g_ccm", rho_in); // Density in g/ccm + pp.get("Ye", Ye_in); // Electron fraction (dimensionless quantity) + pp.get("T_MeV", kT_in); // Temperature in erg + kT_in *= 1e6 * CGSUnitsConst::eV; // convert to cgs : erg + } else if(read_rho_T_Ye_from_table==1){ + // Read background matter from a table + pp.get("background_rho_Ye_T_table_name", background_rho_Ye_T_table_name); + } else AMREX_ASSERT_WITH_MESSAGE(false, "only available read_rho_T_Ye_from_table values are 0 (set rho_g_ccm = ..., Ye = ..., T_MeV = ...) or 1 (provide a table with background data information)"); + + // Do black hole + pp.get("do_blackhole", do_blackhole); + // Black hole properties + if ( do_blackhole == 1 ){ + pp.get("bh_radius", bh_radius); + pp.get("bh_center_x", bh_center_x); + pp.get("bh_center_y", bh_center_y); + pp.get("bh_center_z", bh_center_z); + } + + // Flag for collision term treatment + pp.get("IMFP_method", IMFP_method); + + if(IMFP_method==0) { + // Do nothing + } else if(IMFP_method==1){ + + for(int i=0;i + +#include + +#define H5_USE_16_API 1 +#include "hdf5.h" + +#include "EosTable.H" + +#ifdef AMREX_USE_MPI +#include +#define BCAST(buffer, size) MPI_Bcast(buffer, size, MPI_BYTE, my_reader_process, MPI_COMM_WORLD) +#else +#define BCAST(buffer, size) do { /* do nothing */ } while(0) +#endif + +// Catch HDF5 errors +#define HDF5_ERROR(fn_call) \ + if(doIO) { \ + int _error_code = fn_call; \ + if (_error_code < 0) { \ + AMREX_ASSERT_WITH_MESSAGE(false, "HDF5 call failed"); \ + } \ + } + +using namespace amrex; + +static int file_is_readable(std::string filename); +static int file_is_readable(std::string filename) +{ + FILE* fp = NULL; + fp = fopen(filename.c_str(), "r"); + if(fp != NULL) + { + fclose(fp); + return 1; + } + return 0; +} + +namespace nuc_eos_private { + double *alltables; + double *epstable; + double *logrho; + double *logtemp; + double *yes; + double *helperVarsReal; + int *helperVarsInt; +} + +//TODO: Pass the /path/to/table here in the function argument +void ReadEosTable(const std::string nuceos_table_name) { + using namespace nuc_eos_private; + + amrex::Print() << "(ReadEosTable.cpp) Using table: " << nuceos_table_name << std::endl; + + //TODO: + int my_reader_process = 0; //reader_process; + + const int read_table_on_single_process = 1; + //const int doIO = !read_table_on_single_process || CCTK_MyProc(cctkGH) == my_reader_process; //TODO: + const int doIO = 1; + + hid_t file; + if (doIO && !file_is_readable(nuceos_table_name)) { + AMREX_ASSERT_WITH_MESSAGE(false, "Could not read nuceos_table_name"); + } + + HDF5_ERROR(file = H5Fopen(nuceos_table_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)); + +// Use these two defines to easily read in a lot of variables in the same way +// The first reads in one variable of a given type completely +#define READ_BCAST_EOS_HDF5(NAME,VAR,TYPE,MEM,NELEMS) \ + do { \ + hid_t dataset; \ + HDF5_ERROR(dataset = H5Dopen(file, NAME)); \ + HDF5_ERROR(H5Dread(dataset, TYPE, MEM, H5S_ALL, H5P_DEFAULT, VAR)); \ + if (read_table_on_single_process) \ + BCAST (VAR, sizeof(*(VAR))*(NELEMS)); \ + HDF5_ERROR(H5Dclose(dataset)); \ + } while (0) +// The second reads a given variable into a hyperslab of the alltables_temp array +#define READ_BCAST_EOSTABLE_HDF5(NAME,OFF,DIMS) \ + do { \ + READ_BCAST_EOS_HDF5(NAME,&alltables_temp[(OFF)*(DIMS)[1]],H5T_NATIVE_DOUBLE,H5S_ALL,(DIMS)[1]); \ + } while (0) + + int nrho_; + int ntemp_; + int nye_; + // Read size of tables + READ_BCAST_EOS_HDF5("pointsrho", &nrho_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("pointstemp", &ntemp_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("pointsye", &nye_, H5T_NATIVE_INT, H5S_ALL, 1); + + printf("(ReadEosTable.cpp) nrho = %d, ntemp = %d, nye = %d\n", nrho_, ntemp_, nye_); + + //Allocate managed memory arena on unified memory + ManagedArenaAllocator myManagedArena; + ManagedArenaAllocator myManagedArena_Int; + + // Allocate memory for tables + double *alltables_temp; + if (!(alltables_temp = myManagedArena.allocate(nrho_ * ntemp_ * nye_ * NTABLES) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + if (!(logrho = myManagedArena.allocate(nrho_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + if (!(logtemp = myManagedArena.allocate(ntemp_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + if (!(yes = myManagedArena.allocate(nye_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + + // Prepare HDF5 to read hyperslabs into alltables_temp + hsize_t table_dims[2] = {NTABLES, (hsize_t)nrho_ * ntemp_ * nye_}; + hsize_t var3[2] = { 1, (hsize_t)nrho_ * ntemp_ * nye_}; + hid_t mem3 = H5Screate_simple(2, table_dims, NULL); + + // Read alltables_temp + READ_BCAST_EOSTABLE_HDF5("logpress", 0, table_dims); + READ_BCAST_EOSTABLE_HDF5("logenergy", 1, table_dims); + READ_BCAST_EOSTABLE_HDF5("entropy", 2, table_dims); + READ_BCAST_EOSTABLE_HDF5("munu", 3, table_dims); + READ_BCAST_EOSTABLE_HDF5("cs2", 4, table_dims); + READ_BCAST_EOSTABLE_HDF5("dedt", 5, table_dims); + READ_BCAST_EOSTABLE_HDF5("dpdrhoe", 6, table_dims); + READ_BCAST_EOSTABLE_HDF5("dpderho", 7, table_dims); + // chemical potentials + READ_BCAST_EOSTABLE_HDF5("muhat", 8, table_dims); + READ_BCAST_EOSTABLE_HDF5("mu_e", 9, table_dims); + READ_BCAST_EOSTABLE_HDF5("mu_p", 10, table_dims); + READ_BCAST_EOSTABLE_HDF5("mu_n", 11, table_dims); + // compositions + READ_BCAST_EOSTABLE_HDF5("Xa", 12, table_dims); + READ_BCAST_EOSTABLE_HDF5("Xh", 13, table_dims); + READ_BCAST_EOSTABLE_HDF5("Xn", 14, table_dims); + READ_BCAST_EOSTABLE_HDF5("Xp", 15, table_dims); + // average nucleus + READ_BCAST_EOSTABLE_HDF5("Abar", 16, table_dims); + READ_BCAST_EOSTABLE_HDF5("Zbar", 17, table_dims); + // Gamma + READ_BCAST_EOSTABLE_HDF5("gamma", 18, table_dims); + + double energy_shift_; + // Read additional tables and variables + READ_BCAST_EOS_HDF5("logrho", logrho, H5T_NATIVE_DOUBLE, H5S_ALL, nrho_); + READ_BCAST_EOS_HDF5("logtemp", logtemp, H5T_NATIVE_DOUBLE, H5S_ALL, ntemp_); + READ_BCAST_EOS_HDF5("ye", yes, H5T_NATIVE_DOUBLE, H5S_ALL, nye_); + READ_BCAST_EOS_HDF5("energy_shift", &energy_shift_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + + HDF5_ERROR(H5Sclose(mem3)); + HDF5_ERROR(H5Fclose(file)); + + + // change ordering of alltables array so that + // the table kind is the fastest changing index + if (!(alltables = myManagedArena.allocate(nrho_ * ntemp_ * nye_ * NTABLES) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + + for(int iv = 0;iv epsmax) && (epstable[i] < 1.0e150)){ + epsmax = epstable[i]; + } + if (epstable[i] < epsmin){ + epsmin = epstable[i]; + } + } + + //TODO: Is it correct to subtract energy_shift here? + EOSVAR(eos_epsmin) = epsmin - energy_shift_; + EOSVAR(eos_epsmax) = epsmax - energy_shift_; + + printf("(ReadEosTable.cpp) EOS:rhomin = %.5e g/cm^3\n", EOSVAR(eos_rhomin)); + printf("(ReadEosTable.cpp) EOS:rhomax = %.5e g/cm^3\n", EOSVAR(eos_rhomax)); + printf("(ReadEosTable.cpp) EOS:tempmin = %.4f MeV\n", EOSVAR(eos_tempmin)); + printf("(ReadEosTable.cpp) EOS:tempmax = %.4f MeV\n", EOSVAR(eos_tempmax)); + printf("(ReadEosTable.cpp) EOS:yemin = %.4f\n", EOSVAR(eos_yemin)); + printf("(ReadEosTable.cpp) EOS:yemax = %.4f\n", EOSVAR(eos_yemax)); + + printf("(ReadEosTable.cpp) Finished reading EoS table!\n"); + +} // ReadEOSTable + + + diff --git a/Source/ReadHDF5RhoYeT.H b/Source/ReadHDF5RhoYeT.H new file mode 100644 index 00000000..9b7b6204 --- /dev/null +++ b/Source/ReadHDF5RhoYeT.H @@ -0,0 +1,35 @@ +#ifndef READ_HDF5_RHO_YE_T_H +#define READ_HDF5_RHO_YE_T_H + +struct rhoYeT_input_struct { + + double *rho_input, *Ye_input, *T_input; + + //constructor for Tabulated EOS + AMREX_GPU_DEVICE AMREX_GPU_HOST rhoYeT_input_struct() = default;//Need to keep it + AMREX_GPU_DEVICE AMREX_GPU_HOST rhoYeT_input_struct(double *rho_input, double *Ye_input, double *T_input): + rho_input(rho_input), Ye_input(Ye_input), T_input(T_input) {} + +}; //struct EOS_tabulated + +void ReadInputRhoYeT(const std::string hdf5_background_rho_Ye_T); + +namespace background_input_rho_T_Ye { + +// table data + extern int *n_cell_x; + extern int *n_cell_y; + extern int *n_cell_z; + extern double *x_min; + extern double *x_max; + extern double *y_min; + extern double *y_max; + extern double *z_min; + extern double *z_max; + extern double *rho_array_input; + extern double *T_array_input; + extern double *Ye_array_input; + +} + +#endif // READ_HDF5_RHO_YE_T_H diff --git a/Source/ReadHDF5RhoYeT.cpp b/Source/ReadHDF5RhoYeT.cpp new file mode 100644 index 00000000..727bb44c --- /dev/null +++ b/Source/ReadHDF5RhoYeT.cpp @@ -0,0 +1,171 @@ +#include + +#include + +#define H5_USE_16_API 1 +#include "hdf5.h" + +#include "ReadHDF5RhoYeT.H" + +#ifdef AMREX_USE_MPI +#include +#define BCAST(buffer, size) MPI_Bcast(buffer, size, MPI_BYTE, my_reader_process, MPI_COMM_WORLD) +#else +#define BCAST(buffer, size) do { /* do nothing */ } while(0) +#endif + +// Catch HDF5 errors +#define HDF5_ERROR(fn_call) \ + if(doIO) { \ + int _error_code = fn_call; \ + if (_error_code < 0) { \ + AMREX_ASSERT_WITH_MESSAGE(false, "HDF5 call failed"); \ + } \ + } + +using namespace amrex; + +static int file_is_readable(std::string filename); +static int file_is_readable(std::string filename) +{ + FILE* fp = NULL; + fp = fopen(filename.c_str(), "r"); + if(fp != NULL) + { + fclose(fp); + return 1; + } + return 0; +} + +namespace background_input_rho_T_Ye { + + int *n_cell_x; + int *n_cell_y; + int *n_cell_z; + double *x_min; + double *x_max; + double *y_min; + double *y_max; + double *z_min; + double *z_max; + double *rho_array_input; + double *T_array_input; + double *Ye_array_input; + +} + +//TODO: Pass the /path/to/table here in the function argument +void ReadInputRhoYeT(const std::string hdf5_background_rho_Ye_T){ + using namespace background_input_rho_T_Ye; + + //std::string nuceos_table_name = "/home/sshanka/000_UTK_projects/Emu/Exec/SFHo.h5"; + amrex::Print() << "(ReadHDF5RhoYeT.cpp) Using hdf5: " << hdf5_background_rho_Ye_T << std::endl; + + int my_reader_process = 0; + const int read_table_on_single_process = 1; + const int doIO = 1; + + hid_t file; + if (doIO && !file_is_readable(hdf5_background_rho_Ye_T)) { + AMREX_ASSERT_WITH_MESSAGE(false, "Could not read hdf5_background_rho_Ye_T"); + } + + HDF5_ERROR(file = H5Fopen(hdf5_background_rho_Ye_T.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)); + +// Use these two defines to easily read in a lot of variables in the same way +// The first reads in one variable of a given type completely +#define READ_BCAST_EOS_HDF5(NAME,VAR,TYPE,MEM,NELEMS) \ + do { \ + hid_t dataset; \ + HDF5_ERROR(dataset = H5Dopen(file, NAME)); \ + HDF5_ERROR(H5Dread(dataset, TYPE, MEM, H5S_ALL, H5P_DEFAULT, VAR)); \ + if (read_table_on_single_process) \ + BCAST (VAR, sizeof(*(VAR))*(NELEMS)); \ + HDF5_ERROR(H5Dclose(dataset)); \ + } while (0) +// The second reads a given variable into a hyperslab of the alltables_temp array +#define READ_BCAST_EOSTABLE_HDF5(NAME,OFF,DIMS) \ + do { \ + READ_BCAST_EOS_HDF5(NAME,&allbackgroundYeTrhos_temp[(OFF)*(DIMS)[1]],H5T_NATIVE_DOUBLE,H5S_ALL,(DIMS)[1]); \ + } while (0) + + int ncellx_; + int ncelly_; + int ncellz_; + double xmin_; + double xmax_; + double ymin_; + double ymax_; + double zmin_; + double zmax_; + + // Read size of tables + READ_BCAST_EOS_HDF5("ncellsx", &ncellx_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("ncellsy", &ncelly_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("ncellsz", &ncellz_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("xmin_cm", &xmin_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("ymin_cm", &ymin_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("zmin_cm", &zmin_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("xmax_cm", &xmax_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("ymax_cm", &ymax_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("zmax_cm", &zmax_, H5T_NATIVE_DOUBLE, H5S_ALL, 1); + + printf("(ReadHDF5RhoYeT.cpp) ncellx_ = %d, ncelly_ = %d, ncellz_ = %d\n", ncellx_, ncelly_, ncellz_); + printf("(ReadHDF5RhoYeT.cpp) xmin_ = %f, ymin_ = %f, zmin_ = %f\n", xmin_, ymin_, zmin_); + printf("(ReadHDF5RhoYeT.cpp) xmax_ = %f, ymax_ = %f, zmax_ = %f\n", xmax_, ymax_, zmax_); + + n_cell_x = &ncellx_; + n_cell_y = &ncelly_; + n_cell_z = &ncellz_; + x_min = &xmin_; + x_max = &xmax_; + y_min = &ymin_; + y_max = &ymax_; + z_min = &zmin_; + z_max = &zmax_; + + //Allocate managed memory arena on unified memory + ManagedArenaAllocator myManagedArena; + + // Allocate memory for tables + double *allbackgroundYeTrhos_temp; + if (!(allbackgroundYeTrhos_temp = myManagedArena.allocate(ncellx_ * ncelly_ * ncellz_ * 3 ) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + // Allocate memory for tables + if (!(rho_array_input = myManagedArena.allocate(ncellx_ * ncelly_ * ncellz_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + if (!(T_array_input = myManagedArena.allocate(ncellx_ * ncelly_ * ncellz_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + if (!(Ye_array_input = myManagedArena.allocate(ncellx_ * ncelly_ * ncellz_) )) { + printf("(ReadEosTable.cpp) Cannot allocate memory for EOS table"); + assert(0); + } + + // Prepare HDF5 to read hyperslabs into alltables_temp + hsize_t table_dims[2] = {3, (hsize_t)ncellx_ * ncelly_ * ncellz_}; + hid_t mem3 = H5Screate_simple(2, table_dims, NULL); + + // Read alltables_temp + READ_BCAST_EOSTABLE_HDF5("rho_g|ccm", 0, table_dims); + READ_BCAST_EOSTABLE_HDF5("T_Mev", 1, table_dims); + READ_BCAST_EOSTABLE_HDF5("Ye", 2, table_dims); + + HDF5_ERROR(H5Fclose(file)); + + for(int i = 0 ; i < ncellx_ * ncelly_ * ncellz_ ; i++ ) { + rho_array_input[i] = allbackgroundYeTrhos_temp[ i + 0 * ncellx_ * ncelly_ * ncellz_ ]; + T_array_input [i] = allbackgroundYeTrhos_temp[ i + 1 * ncellx_ * ncelly_ * ncellz_ ]; + Ye_array_input [i] = allbackgroundYeTrhos_temp[ i + 2 * ncellx_ * ncelly_ * ncellz_ ]; + } + + // free memory of temporary array + myManagedArena.deallocate(allbackgroundYeTrhos_temp, ncellx_ * ncelly_ * ncellz_ * 3); + +} // ReadEOSTable \ No newline at end of file diff --git a/Source/ReadInput_RhoTempYe.H b/Source/ReadInput_RhoTempYe.H new file mode 100644 index 00000000..b9dd6113 --- /dev/null +++ b/Source/ReadInput_RhoTempYe.H @@ -0,0 +1,14 @@ +#ifndef READINPUT_RHOTEMPYE_H +#define READINPUT_RHOTEMPYE_H + +#include +#include +#include +#include +#include +#include + +void set_rho_T_Ye(MultiFab& state, const Geometry& geom, const TestParams* parms); + + +#endif diff --git a/Source/ReadInput_RhoTempYe.cpp b/Source/ReadInput_RhoTempYe.cpp new file mode 100644 index 00000000..aca0f93d --- /dev/null +++ b/Source/ReadInput_RhoTempYe.cpp @@ -0,0 +1,67 @@ +#include "Evolve.H" +#include "Constants.H" +#include "ReadHDF5RhoYeT.H" +#include "ReadInput_RhoTempYe.H" +#include + +void set_rho_T_Ye(MultiFab& state, const Geometry& geom, const TestParams* parms) +{ + // Create an alias of the MultiFab so set_rho_T_Ye only sets rho, T and Ye. + int start_comp = GIdx::rho; + int num_comps = 3; //We only want to set GIdx::rho, GIdx::T and GIdx::Ye + MultiFab rho_T_ye_state(state, amrex::make_alias, start_comp, num_comps); + + amrex::GpuArray dx = geom.CellSizeArray(); + + ReadInputRhoYeT(parms->background_rho_Ye_T_table_name); + + using namespace background_input_rho_T_Ye; + int ncell_x = *n_cell_x; + int ncell_y = *n_cell_y; + int ncell_z = *n_cell_z; + double xmin_ = *x_min; + double xmax_ = *x_max; + double ymin_ = *y_min; + double ymax_ = *y_max; + double zmin_ = *z_min; + double zmax_ = *z_max; + double lx = xmax_ - xmin_; + double ly = ymax_ - ymin_; + double lz = zmax_ - zmin_; + + if (ncell_x != parms->ncell[0] || ncell_y != parms->ncell[1] || ncell_z != parms->ncell[2]) { + amrex::Print() << "The number of cells in the background data file does not match the parameter file" << std::endl; + abort(); + } + + if (lx != parms->Lx || ly != parms->Ly || lz != parms->Lz ) { + amrex::Print() << "The simulation domain in the background data file does not match the parameter file" << std::endl; + abort(); + } + + rhoYeT_input_struct rhoYeT_input_obj(rho_array_input, Ye_array_input, T_array_input); + + for(amrex::MFIter mfi(rho_T_ye_state); mfi.isValid(); ++mfi){ + const amrex::Box& bx = mfi.validbox(); + const amrex::Array4& mf_array = rho_T_ye_state.array(mfi); + + amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k){ + + int ig = i; + int jg = j; + int kg = k; + + // Compute the 1D index from 3D coordinates in the linearized array + int idx = kg + ncell_z * (jg + ncell_y * ig); + + // Set the values from the input arrays + mf_array(i, j, k, GIdx::rho - start_comp) = rhoYeT_input_obj.rho_input[idx]; // g/ccm + mf_array(i, j, k, GIdx::T - start_comp) = rhoYeT_input_obj.T_input[idx]*1e6*CGSUnitsConst::eV; //erg + mf_array(i, j, k, GIdx::Ye - start_comp) = rhoYeT_input_obj.Ye_input[idx]; + + }); + } + +} + + diff --git a/Source/ReadNuLibTable.cpp b/Source/ReadNuLibTable.cpp new file mode 100644 index 00000000..bbed8577 --- /dev/null +++ b/Source/ReadNuLibTable.cpp @@ -0,0 +1,281 @@ + #include + +#include + +#define H5_USE_16_API 1 +#include "hdf5.h" + +#include "NuLibTable.H" + +#ifdef AMREX_USE_MPI +#include +#define BCAST(buffer, size) MPI_Bcast(buffer, size, MPI_BYTE, my_reader_process, MPI_COMM_WORLD) +#else +#define BCAST(buffer, size) do { /* do nothing */ } while(0) +#endif + +// Catch HDF5 errors +#define HDF5_ERROR(fn_call) \ + if(doIO) { \ + int _error_code = fn_call; \ + if (_error_code < 0) { \ + AMREX_ASSERT_WITH_MESSAGE(false, "HDF5 call failed"); \ + } \ + } + +using namespace amrex; + +static int file_is_readable(std::string filename); +static int file_is_readable(std::string filename) +{ + FILE* fp = NULL; + fp = fopen(filename.c_str(), "r"); + if(fp != NULL) + { + fclose(fp); + return 1; + } + return 0; +} + +namespace nulib_private { + double *alltables_nulib; + //double *epstable; + double *logrho_nulib; + double *logtemp_nulib; + double *yes_nulib; + double *species_nulib; //TODO: Get rid of this? + double *group_nulib; + double *helperVarsReal_nulib; + int *helperVarsInt_nulib; +} + +void ReadNuLibTable(const std::string nulib_table_name) { + using namespace nulib_private; + + amrex::Print() << "(ReadNuLibTable.cpp) Using table: " << nulib_table_name << std::endl; + + //TODO: + int my_reader_process = 0; //reader_process; + + const int read_table_on_single_process = 1; + //const int doIO = !read_table_on_single_process || CCTK_MyProc(cctkGH) == my_reader_process; //TODO: + const int doIO = 1; + + hid_t file; + if (doIO && !file_is_readable(nulib_table_name)) { + AMREX_ASSERT_WITH_MESSAGE(false, "Could not read nulib_table_name"); + } + + HDF5_ERROR(file = H5Fopen(nulib_table_name.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT)); + +// Use these two defines to easily read in a lot of variables in the same way +// The first reads in one variable of a given type completely +#define READ_BCAST_EOS_HDF5(NAME,VAR,TYPE,MEM,NELEMS) \ + do { \ + hid_t dataset; \ + HDF5_ERROR(dataset = H5Dopen(file, NAME)); \ + HDF5_ERROR(H5Dread(dataset, TYPE, MEM, H5S_ALL, H5P_DEFAULT, VAR)); \ + if (read_table_on_single_process) \ + BCAST (VAR, sizeof(*(VAR))*(NELEMS)); \ + HDF5_ERROR(H5Dclose(dataset)); \ + } while (0) +// The second reads a given variable into a hyperslab of the alltables_temp array +#define READ_BCAST_EOSTABLE_HDF5(NAME,OFF,DIMS) \ + do { \ + READ_BCAST_EOS_HDF5(NAME,&alltables_temp[(OFF)*(DIMS)[1]],H5T_NATIVE_DOUBLE,H5S_ALL,(DIMS)[1]); \ + } while (0) + + int nrho_; + int ntemp_; + int nye_; + int nspecies_; + int ngroup_; + + // Read size of tables + READ_BCAST_EOS_HDF5("nrho", &nrho_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("ntemp", &ntemp_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("nye", &nye_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("number_species", &nspecies_, H5T_NATIVE_INT, H5S_ALL, 1); + READ_BCAST_EOS_HDF5("number_groups", &ngroup_, H5T_NATIVE_INT, H5S_ALL, 1); + + assert(nspecies_ == 2); //For now, the code only works when NuLib table has (e,a,x) values. + + printf("(ReadNuLibTable.cpp) nrho = %d, ntemp = %d, nye = %d, nspecies=%d, ngroup=%d\n", nrho_, ntemp_, nye_, nspecies_, ngroup_); + + //Allocate managed memory arena on unified memory + ManagedArenaAllocator myManagedArena; + ManagedArenaAllocator myManagedArena_Int; + + // Allocate memory for tables + double *alltables_temp; + if (!(alltables_temp = myManagedArena.allocate(nrho_ * ntemp_ * nye_ * nspecies_ * ngroup_ * NTABLES_NULIB) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(logrho_nulib = myManagedArena.allocate(nrho_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(logtemp_nulib = myManagedArena.allocate(ntemp_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(yes_nulib = myManagedArena.allocate(nye_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(species_nulib = myManagedArena.allocate(nspecies_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(group_nulib = myManagedArena.allocate(ngroup_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + + //Allocate memory for energy bin determination. + double *energy_bottom; + double *energy_top; + if (!(energy_bottom = myManagedArena.allocate(ngroup_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + if (!(energy_top = myManagedArena.allocate(ngroup_) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + + // Prepare HDF5 to read hyperslabs into alltables_temp + hsize_t table_dims[2] = {NTABLES_NULIB, (hsize_t)nrho_ * ntemp_ * nye_ * nspecies_ * ngroup_}; + //hsize_t var3[2] = { 1, (hsize_t)nrho_ * ntemp_ * nye_ * nspecies_ * ngroup_}; + hid_t mem3 = H5Screate_simple(2, table_dims, NULL); + + // Read alltables_temp + READ_BCAST_EOSTABLE_HDF5("absorption_opacity", 0, table_dims); + READ_BCAST_EOSTABLE_HDF5("scattering_opacity", 1, table_dims); + + // Read additional tables and variables + //This is not log yet. + READ_BCAST_EOS_HDF5("rho_points", logrho_nulib, H5T_NATIVE_DOUBLE, H5S_ALL, nrho_); + READ_BCAST_EOS_HDF5("temp_points", logtemp_nulib, H5T_NATIVE_DOUBLE, H5S_ALL, ntemp_); + READ_BCAST_EOS_HDF5("ye_points", yes_nulib, H5T_NATIVE_DOUBLE, H5S_ALL, nye_); + READ_BCAST_EOS_HDF5("neutrino_energies", group_nulib, H5T_NATIVE_DOUBLE, H5S_ALL, ngroup_); + + READ_BCAST_EOS_HDF5("bin_bottom", energy_bottom, H5T_NATIVE_DOUBLE, H5S_ALL, ngroup_); + READ_BCAST_EOS_HDF5("bin_top", energy_top, H5T_NATIVE_DOUBLE, H5S_ALL, ngroup_); + + HDF5_ERROR(H5Sclose(mem3)); + HDF5_ERROR(H5Fclose(file)); + + // change ordering of alltables_nulib array so that + // the table kind is the fastest changing index + if (!(alltables_nulib = myManagedArena.allocate(nrho_ * ntemp_ * nye_ * nspecies_ * ngroup_ * NTABLES_NULIB) )) { + printf("(ReadNuLibTable.cpp) Cannot allocate memory for NuLib table"); + assert(0); + } + + for(int iv = 0;iv= energy_bottom[i] && given_energy <= energy_top[i]){ + idx_group_ = i; + break; + } + } + + printf("Given neutrino energy = %f, selected bin index = %d\n", given_energy, idx_group); + myManagedArena.deallocate(energy_bottom, ngroup_); + myManagedArena.deallocate(energy_top, ngroup_); + //---------------------------------------------------------------------------------------------- + + //allocate memory for helperVars + helperVarsReal_nulib = myManagedArena.allocate(24); + helperVarsInt_nulib = myManagedArena_Int.allocate(5); + + const double temp0_ = exp(logtemp_nulib[0]); + const double temp1_ = exp(logtemp_nulib[1]); + + NULIBVAR(idx_group) = idx_group_; + + NULIBVAR_INT(nrho) = nrho_; + NULIBVAR_INT(ntemp) = ntemp_; + NULIBVAR_INT(nye) = nye_; + NULIBVAR_INT(nspecies) = nspecies_; + NULIBVAR_INT(ngroup) = ngroup_; + + // set up some vars + NULIBVAR(dtemp) = (logtemp_nulib[ntemp_-1] - logtemp_nulib[0]) / (1.0*(ntemp_-1)); + NULIBVAR(dtempi) = 1.0/NULIBVAR(dtemp); + + NULIBVAR(dlintemp) = temp1_ - temp0_; + NULIBVAR(dlintempi) = 1.0/NULIBVAR(dlintemp); + + NULIBVAR(drho) = (logrho_nulib[nrho_-1] - logrho_nulib[0]) / (1.0*(nrho_-1)); + NULIBVAR(drhoi) = 1.0/NULIBVAR(drho); + + NULIBVAR(dye) = (yes_nulib[nye_-1] - yes_nulib[0]) / (1.0*(nye_-1)); + NULIBVAR(dyei) = 1.0/NULIBVAR(dye); + + NULIBVAR(drhotempi) = NULIBVAR(drhoi) * NULIBVAR(dtempi); + NULIBVAR(drholintempi) = NULIBVAR(drhoi) * NULIBVAR(dlintempi); + NULIBVAR(drhoyei) = NULIBVAR(drhoi) * NULIBVAR(dyei); + NULIBVAR(dtempyei) = NULIBVAR(dtempi) * NULIBVAR(dyei); + NULIBVAR(dlintempyei) = NULIBVAR(dlintempi) * NULIBVAR(dyei); + NULIBVAR(drhotempyei) = NULIBVAR(drhoi) * NULIBVAR(dtempi) * NULIBVAR(dyei); + NULIBVAR(drholintempyei) = NULIBVAR(drhoi) * NULIBVAR(dlintempi) * NULIBVAR(dyei); + + //helperVarsReal_nulib[helperVarsEnumReal::eos_rhomax] = exp(logrho_nulib[nrho_-1]); + NULIBVAR(eos_rhomax) = exp(logrho_nulib[nrho_-1]); + NULIBVAR(eos_rhomin) = exp(logrho_nulib[0]); + + NULIBVAR(eos_tempmax) = exp(logtemp_nulib[ntemp_-1]); + NULIBVAR(eos_tempmin) = exp(logtemp_nulib[0]); + + NULIBVAR(eos_yemax) = yes_nulib[nye_-1]; + NULIBVAR(eos_yemin) = yes_nulib[0]; + + + printf("(ReadNuLibTable.cpp) NuLib:rhomin = %.5e g/cm^3\n", NULIBVAR(eos_rhomin)); + printf("(ReadNuLibTable.cpp) NuLib:rhomax = %.5e g/cm^3\n", NULIBVAR(eos_rhomax)); + printf("(ReadNuLibTable.cpp) NuLib:tempmin = %.4f MeV\n", NULIBVAR(eos_tempmin)); + printf("(ReadNuLibTable.cpp) NuLib:tempmax = %.4f MeV\n", NULIBVAR(eos_tempmax)); + printf("(ReadNuLibTable.cpp) NuLib:yemin = %.4f\n", NULIBVAR(eos_yemin)); + printf("(ReadNuLibTable.cpp) NuLib:yemax = %.4f\n", NULIBVAR(eos_yemax)); + + printf("(ReadNuLibTable.cpp) Finished reading NuLib table!\n"); + +} // ReadNuLibTable + + + diff --git a/Source/main.cpp b/Source/main.cpp index edd2d6c3..e5ca0a9c 100644 --- a/Source/main.cpp +++ b/Source/main.cpp @@ -31,16 +31,49 @@ #include "Evolve.H" #include "Constants.H" #include "IO.H" +#include "DataReducer.H" +#include "EosTable.H" +#include "NuLibTable.H" +#include "ReadInput_RhoTempYe.H" using namespace amrex; void evolve_flavor(const TestParams* parms) { - // Periodicity and Boundary Conditions - // Defaults to Periodic in all dimensions - Vector is_periodic(AMREX_SPACEDIM, 1); + + //The BC will be set using parameter file. + //Option 0: use periodic BC + //Option 1: create particles at boundary. + + const int BC_type = parms->boundary_condition_type; //0=periodic, 1=outer. + + int BC_type_val; + enum BC_type_enum {PERIODIC, OUTER}; + + if (BC_type == 0){ + BC_type_val = BC_type_enum::PERIODIC; //use periodic BC + } else if (BC_type == 1){ + BC_type_val = BC_type_enum::OUTER; //use outer BC + } else { + amrex::Abort("BC_type is incorrect."); + } + + int periodic_flag; + if (BC_type_val == BC_type_enum::PERIODIC){ + //1=yes, use periodic + periodic_flag = 1; + } else if (BC_type_val == BC_type_enum::OUTER){ + //2=no, do not use periodic. + periodic_flag = 0; + } else { + amrex::Abort("BC_type is incorrect."); + } + + Vector is_periodic(AMREX_SPACEDIM, periodic_flag); + Vector domain_lo_bc_types(AMREX_SPACEDIM, BCType::int_dir); Vector domain_hi_bc_types(AMREX_SPACEDIM, BCType::int_dir); + // Define the index space of the domain @@ -79,16 +112,35 @@ void evolve_flavor(const TestParams* parms) // initialize with NaNs ... state.setVal(0.0); - state.setVal(parms->rho_in,GIdx::rho,1); // g/ccm - state.setVal(parms->Ye_in,GIdx::Ye,1); - state.setVal(parms->T_in,GIdx::T,1); // MeV - state.FillBoundary(geom.periodicity()); + //If reading from table, call function "set_rho_T_Ye". + //Else set rho, T and Ye to constant value throughout the grid using values from parameter file. + if (parms->read_rho_T_Ye_from_table){ + set_rho_T_Ye(state, geom, parms); + } else { + state.setVal(parms->rho_in,GIdx::rho,1); // g/ccm + state.setVal(parms->Ye_in,GIdx::Ye,1); + state.setVal(parms->kT_in,GIdx::T,1); // erg + } + + state.FillBoundary(geom.periodicity()); + // initialize the grid variable names GIdx::Initialize(); + //We only need HDF5 tables if IMFP_method is 2. + if(parms->IMFP_method==2){ + // read the EoS table + amrex::Print() << "Reading EoS table... " << std::endl; + ReadEosTable(parms->nuceos_table_name); + + // read the NuLib table + amrex::Print() << "Reading NuLib table... " << std::endl; + ReadNuLibTable(parms->nulib_table_name); + } + // Initialize particles on the domain - amrex::Print() << "Initializing particles... "; + amrex::Print() << "Initializing particles... " << std::endl; // We store old-time and new-time data FlavoredNeutrinoContainer neutrinos_old(geom, dm, ba); @@ -116,20 +168,22 @@ void evolve_flavor(const TestParams* parms) // Deposit particles to grid deposit_to_mesh(neutrinos_old, state, geom); - + // Write plotfile after initialization + DataReducer rd; if (not parms->do_restart) { // If we have just initialized, then always save the particle data for reference - const int write_particles_after_init = 1; + const int write_particles_after_init = (parms->write_plot_particles_every>0); WritePlotFile(state, neutrinos_old, geom, initial_time, initial_step, write_particles_after_init); + rd.InitializeFiles(); } amrex::Print() << "Done. " << std::endl; - TimeIntegrator integrator(neutrinos_old, neutrinos_new, initial_time, initial_step); + TimeIntegrator integrator(neutrinos_old); // Create a RHS source function we will integrate - auto source_fun = [&] (FlavoredNeutrinoContainer& neutrinos_rhs, const FlavoredNeutrinoContainer& neutrinos, Real time) { + auto source_fun = [&] (FlavoredNeutrinoContainer& neutrinos_rhs, const FlavoredNeutrinoContainer& neutrinos, Real /* time */) { /* Evaluate the neutrino distribution matrix RHS */ // Step 1: Deposit Particle Data to Mesh & fill domain boundaries/ghost cells @@ -145,6 +199,7 @@ void evolve_flavor(const TestParams* parms) // B) We only Redistribute the integrator new data at the end of the timestep, not all the RHS data. // Thus, this copy clears the old RHS particles and creates particles in the RHS container corresponding // to the current particles in neutrinos. + neutrinos_rhs.copyParticles(neutrinos, true); // Step 3: Interpolate Mesh to construct the neutrino RHS in place @@ -155,8 +210,31 @@ void evolve_flavor(const TestParams* parms) auto post_timestep_fun = [&] () { /* Post-timestep function. The integrator new-time data is the latest data available. */ - // Get the latest neutrino data - auto& neutrinos = integrator.get_new_data(); + // Use the latest-time neutrino data + auto& neutrinos = neutrinos_new; + + // If do_periodic_empty_bc is one. + // Do periodic boundary conditions but initialize particles with N=0 and Nbar=0 at the boundary. + // If a black hole is present in the simulation it will set N=0 and Nbar=0 for all particles inside the black hole. + if ( parms->do_periodic_empty_bc == 1 ){ + empty_particles_at_boundary_cells(neutrinos, parms); + } + + const Real current_dt = integrator.get_timestep(); //FIXME: FIXME: Pass this to neutrinos.CreateParticlesAtBoundary. + + //FIXME: Think carefully where to call this function. + //Create particles at outer boundary + if (BC_type_val == BC_type_enum::OUTER){ + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + neutrinos.CreateParticlesAtBoundary(parms, current_dt); + } + + //Create particles at inner boundary + //TODO: This needs to be implemented. // Update the new time particle locations in the domain with their // integrated coordinates. @@ -169,21 +247,20 @@ void evolve_flavor(const TestParams* parms) // since Redistribute() applies periodic boundary conditions. neutrinos.SyncLocation(Sync::PositionToCoordinate); - // Renormalize the neutrino state - neutrinos.Renormalize(parms); - // Get which step the integrator is on const int step = integrator.get_step_number(); const Real time = integrator.get_time(); - amrex::Print() << "Completed time step: " << step << " t = " << time << " s. ct = " << PhysConst::c * time << " cm" << std::endl; + printf("Writing reduced data to file... \n"); + rd.WriteReducedData0D(geom, state, neutrinos, time, step+1); + printf("Done. \n"); run_fom += neutrinos.TotalNumberOfParticles(); // Write the Mesh Data to Plotfile if required - if ((step+1) % parms->write_plot_every == 0 || - (parms->write_plot_particles_every > 0 && - (step+1) % parms->write_plot_particles_every == 0)) { + bool write_plotfile = parms->write_plot_every > 0 && (step+1) % parms->write_plot_every == 0; + bool write_plot_particles = parms->write_plot_particles_every > 0 && (step+1) % parms->write_plot_particles_every == 0; + if (write_plotfile || write_plot_particles) { // Only include the Particle Data if write_plot_particles_every is satisfied int write_plot_particles = parms->write_plot_particles_every > 0 && (step+1) % parms->write_plot_particles_every == 0; @@ -194,8 +271,11 @@ void evolve_flavor(const TestParams* parms) // Note: this won't be the same as the new-time grid data // because the last deposit_to_mesh call was at either the old time (forward Euler) // or the final RK stage, if using Runge-Kutta. - const Real dt = compute_dt(geom,parms->cfl_factor,state,neutrinos,parms->flavor_cfl_factor,parms->max_adaptive_speedup); + printf("Setting next timestep... \n"); + const Real dt = compute_dt(geom, state, neutrinos, parms); integrator.set_timestep(dt); + //printf("current_dt = %g, dt = %g \n", current_dt, dt); + printf("Done. \n"); }; // Attach our RHS and post timestep hooks to the integrator @@ -203,14 +283,14 @@ void evolve_flavor(const TestParams* parms) integrator.set_post_timestep(post_timestep_fun); // Get a starting timestep - const Real starting_dt = compute_dt(geom,parms->cfl_factor,state,neutrinos_old,parms->flavor_cfl_factor, parms->max_adaptive_speedup); + const Real starting_dt = compute_dt(geom, state, neutrinos_old, parms); // Do all the science! amrex::Print() << "Starting timestepping loop... " << std::endl; Real start_time = amrex::second(); - integrator.integrate(starting_dt, parms->end_time, parms->nsteps); + integrator.integrate(neutrinos_old, neutrinos_new, initial_time, starting_dt, parms->end_time, initial_step, parms->nsteps); Real stop_time = amrex::second(); Real advance_time = stop_time - start_time; @@ -228,8 +308,19 @@ void evolve_flavor(const TestParams* parms) int main(int argc, char* argv[]) { + //In amrex::Initialize, a large amount of GPU device memory is allocated and is kept in The_Arena(). + //The default is 3/4 of the total device memory. + //It can be changed with a ParmParse parameter, amrex.the_arena_init_size, in the unit of bytes. + //The default initial size for other arenas is 8388608 (i.e., 8 MB). + ParmParse pp; + pp.add("amrex.the_arena_init_size", 8388608); + pp.add("amrex.the_managed_arena_init_size", 8388608); + pp.add("amrex.the_device_arena_init_size", 8388608); + amrex::Initialize(argc,argv); + MFIter::allowMultipleMFIters(true); + // write build information to screen if (ParallelDescriptor::IOProcessor()) { writeBuildInfo(); diff --git a/amrex b/amrex deleted file mode 160000 index ee5c09d4..00000000 --- a/amrex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ee5c09d41e1f4bd9f1a3754b4c513b76322cd20e diff --git a/makefiles/GNUmakefile_bridges b/makefiles/GNUmakefile_bridges index 749190a8..5eb8631e 100644 --- a/makefiles/GNUmakefile_bridges +++ b/makefiles/GNUmakefile_bridges @@ -1,8 +1,8 @@ #module unload intel #module load python3/3.5.2_gcc_mkl cuda/10.2 mpi/gcc_openmpi gcc/8.3.0 -EMU_HOME ?= ../ -AMREX_HOME ?= ../amrex +OUTPUT_PRESSURE = 0 +NUM_MOMENTS = 2 DIM = 3 diff --git a/makefiles/GNUmakefile_cassowary b/makefiles/GNUmakefile_cassowary new file mode 100644 index 00000000..6ab77307 --- /dev/null +++ b/makefiles/GNUmakefile_cassowary @@ -0,0 +1,24 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS=2 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = TRUE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = FALSE +AMREX_CUDA_ARCH=70 + +USE_HDF5=TRUE +ifeq ($(USE_MPI),TRUE) +HDF5_HOME=/usr/lib/x86_64-linux-gnu/hdf5/openmpi +else +HDF5_HOME=/usr/lib/x86_64-linux-gnu/hdf5/serial +endif + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_default b/makefiles/GNUmakefile_default new file mode 100644 index 00000000..48f6bae7 --- /dev/null +++ b/makefiles/GNUmakefile_default @@ -0,0 +1,19 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS = 2 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = FALSE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = FALSE + +USE_HDF5 = TRUE +HDF5_HOME= /path/to/hdf5 + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_ganon b/makefiles/GNUmakefile_ganon deleted file mode 100644 index e6c6fa45..00000000 --- a/makefiles/GNUmakefile_ganon +++ /dev/null @@ -1,33 +0,0 @@ -EMU_HOME ?= ../ -AMREX_HOME ?= ../amrex - -DIM = 3 - -NUM_FLAVORS = 2 - -COMP = gnu - -DEBUG = TRUE - -USE_MPI = TRUE -USE_OMP = FALSE -USE_ACC = FALSE -USE_CUDA = FALSE -USE_HDF5 = FALSE - -TINY_PROFILE = TRUE -USE_PARTICLES = TRUE - -PRECISION = DOUBLE - -Bpack := -Blocs := . - -ifeq ($(USE_HDF5), TRUE) -HDF5_HOME = /usr/local/hdf5-1.12.0_gnu7.5.0 -DEFINES += -DAMREX_USE_HDF5 -INCLUDE_LOCATIONS += $(HDF5_HOME)/include -LIBRARIES += -L$(HDF5_HOME)/lib -lhdf5 -lz -ldl -endif - -include ../Make.Emu diff --git a/makefiles/GNUmakefile_jenkins b/makefiles/GNUmakefile_jenkins new file mode 100644 index 00000000..d8d1143d --- /dev/null +++ b/makefiles/GNUmakefile_jenkins @@ -0,0 +1,17 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS = 3 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = TRUE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = TRUE +AMREX_CUDA_ARCH=60 + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_jenkins_HDF5 b/makefiles/GNUmakefile_jenkins_HDF5 new file mode 100644 index 00000000..242f5e6b --- /dev/null +++ b/makefiles/GNUmakefile_jenkins_HDF5 @@ -0,0 +1,20 @@ +NUM_FLAVORS = 3 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS = 3 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = TRUE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = FALSE +AMREX_CUDA_ARCH=60 + +USE_HDF5=TRUE +HDF5_HOME=/usr/lib/x86_64-linux-gnu/hdf5/openmpi + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_jenkins_HDF5_CUDA b/makefiles/GNUmakefile_jenkins_HDF5_CUDA new file mode 100644 index 00000000..72f9e71a --- /dev/null +++ b/makefiles/GNUmakefile_jenkins_HDF5_CUDA @@ -0,0 +1,20 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS = 3 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = TRUE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = TRUE +AMREX_CUDA_ARCH=60 + +USE_HDF5=TRUE +HDF5_HOME=/usr/lib/x86_64-linux-gnu/hdf5/openmpi + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_macOS b/makefiles/GNUmakefile_macOS new file mode 100644 index 00000000..a87167e3 --- /dev/null +++ b/makefiles/GNUmakefile_macOS @@ -0,0 +1,22 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = FALSE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = FALSE +USE_HDF5 = FALSE + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu + +CXX = mpicxx +CC = mpicc +FC = gfortran-13 +F90 = gfortran-13 +INCLUDE_LOCATIONS += /usr/local/include diff --git a/makefiles/GNUmakefile_perlmutter_HDF5_CUDA b/makefiles/GNUmakefile_perlmutter_HDF5_CUDA new file mode 100644 index 00000000..93c7005a --- /dev/null +++ b/makefiles/GNUmakefile_perlmutter_HDF5_CUDA @@ -0,0 +1,20 @@ +NUM_FLAVORS = 2 +SHAPE_FACTOR_ORDER = 2 +NUM_MOMENTS = 3 + +COMP = gnu + +DEBUG = FALSE + +USE_MPI = TRUE +USE_OMP = FALSE +USE_ACC = FALSE +USE_CUDA = TRUE +AMREX_CUDA_ARCH=80 + +USE_HDF5=TRUE +HDF5_HOME=/opt/cray/pe/hdf5-parallel/1.12.2.9/ + +EMU_HOME = .. +AMREX_HOME = ../submodules/amrex +include ../Make.Emu diff --git a/makefiles/GNUmakefile_travis b/makefiles/GNUmakefile_travis deleted file mode 100644 index 880ccf94..00000000 --- a/makefiles/GNUmakefile_travis +++ /dev/null @@ -1,27 +0,0 @@ -EMU_HOME ?= ../ -AMREX_HOME ?= ../amrex - -DIM = 3 - -NUM_FLAVORS = 2 - -SHAPE_FACTOR_ORDER = 2 - -COMP = gnu - -DEBUG = TRUE - -USE_MPI = TRUE -USE_OMP = FALSE -USE_ACC = FALSE -USE_CUDA = FALSE - -TINY_PROFILE = TRUE -USE_PARTICLES = TRUE - -PRECISION = DOUBLE - -Bpack := -Blocs := . - -include ../Make.Emu diff --git a/sample_inputs/inputs_1d_fiducial b/sample_inputs/inputs_1d_fiducial index 72ee3bfb..a8148b92 100644 --- a/sample_inputs/inputs_1d_fiducial +++ b/sample_inputs/inputs_1d_fiducial @@ -1,14 +1,10 @@ -simulation_type = 4 -st4_theta = 0 -st4_thetabar = 3.14159265359 -st4_phi = 0 -st4_phibar=0 -st4_ndens = 4.891290819e+32 -st4_ndensbar = 4.891290819e+32 -st4_fluxfac = .333333333333333 -st4_fluxfacbar = .333333333333333 -st4_amplitude = 1e-6 +perturbation_type = 0 +perturbation_amplitude = 1e-6 +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1 + +collision_cfl_factor = 1e-3 cfl_factor = 0.5 flavor_cfl_factor = 0.5 max_adaptive_speedup = 0 @@ -25,13 +21,13 @@ Lz = 64.0 # Number of particles per cell nppc = (1, 1, 1) -nphi_equator = 16 +particle_data_filename = "particle_input.dat" # Maximum size of each grid in the domain max_grid_size = 16 # Number of steps to run -nsteps = 5000 +nsteps = 1000 # Simulation end time end_time = 5.0e-9 @@ -86,3 +82,23 @@ alpha2_degrees = 0 # CP-violating phase in degrees [NO:222 IO:285] deltaCP_degrees = 222 +################# +# opacity stuff # +################# +IMFP_method = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_bc_periodic_init b/sample_inputs/inputs_bc_periodic_init new file mode 100644 index 00000000..bbc2ad0b --- /dev/null +++ b/sample_inputs/inputs_bc_periodic_init @@ -0,0 +1,133 @@ +perturbation_type = 0 +perturbation_amplitude = 0.0 + +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 0 + +collision_cfl_factor = 1e-1 +cfl_factor = 2 +flavor_cfl_factor = 2 +max_adaptive_speedup = 0 +maxError = 1e-6 + +integration.type = 1 +integration.rk.type = 4 + +# Domain size in 3D index space +ncell = (5, 5, 5) +Lx = 5 # cm +Ly = 5 # cm +Lz = 5 # cm + +# Number of particles per cell +nppc = (1, 1, 1) +particle_data_filename = "particle_input.dat" + +# Maximum size of each grid in the domain +max_grid_size = 16 + +# Number of steps to run +nsteps = 30000 + +# Simulation end time +end_time = 5.0e19 + +# Make FPE signal errors so we get a Backtrace +amrex.fpe_trap_invalid=1 + +# give background fluid conditions +rho_g_ccm = 0 +T_MeV = 7.0 +Ye = 0 + +# Write plotfiles +write_plot_every = 3000 + +# Write particle data in plotfiles +write_plot_particles_every = 3000 + +# checkpointing +do_restart = 0 +restart_dir = "" + +############################### +# NEUTRINO PHYSICS PARAMETERS # +############################### +# see first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf + +# mass state 1 mass in eV [NO/IO:-sqrt(7.39e-5)] +mass1_eV = 0.04866 #-0.008596511 + +# mass state 2 mass in eV (define at 0 arbitrarily because oscillations only sensitive to deltaM^2) +mass2_eV = 0 + +# mass state 3 mass in eV [NO:sqrt(2.449e-3) IO:-sqrt(2.509e-3)] +mass3_eV = 0 + +# 1-2 mixing angle in degrees [NO/IO:33.82] +theta12_degrees = 1e-6 + +# 2-3 mixing angle in degrees [NO:8.61 IO:8.65] +theta23_degrees = 0 + +# 1-3 mixing angle in degrees [NO:48.3 IO:48.6] +theta13_degrees = 0 + +# Majorana angle 1 in degrees +alpha1_degrees = 0 + +# Majorana angle 2 in degrees +alpha2_degrees = 0 + +# CP-violating phase in degrees [NO:222 IO:285] +deltaCP_degrees = 0 + +################# +# opacity stuff # +################# +IMFP_method = 1 + +Do_Pauli_blocking = 0 # If 1, it will multiply the inverse mean free path by 1 / (1 - f_eq); if 0, do nothing. + +IMFP_abs0_cm = 1.0e2 +IMFP_abs1_cm = 0 +IMFP_abs2_cm = 0 +IMFP_abs0bar_cm = 1.0e2 +IMFP_abs1bar_cm = 0 +IMFP_abs2bar_cm = 0 + +munu0_MeV = 20.0 +munu1_MeV = 0 +munu2_MeV = 0 +munu0bar_MeV = 17.644694342915507 +munu1bar_MeV = 0 +munu2bar_MeV = 0 + +IMFP_scat0_cm = 0 +IMFP_scat1_cm = 0 +IMFP_scat2_cm = 0 +IMFP_scat0bar_cm = 0 +IMFP_scat1bar_cm = 0 +IMFP_scat2bar_cm = 0 + +delta_E = 2.272540842052914 # Mev + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 1 +bh_radius = 0.5 # cm +bh_center_x = 2.5 # cm +bh_center_y = 2.5 # cm +bh_center_z = 2.5 # cm + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 1 \ No newline at end of file diff --git a/sample_inputs/inputs_bipolar_test b/sample_inputs/inputs_bipolar_test index 305ddf47..9781389a 100644 --- a/sample_inputs/inputs_bipolar_test +++ b/sample_inputs/inputs_bipolar_test @@ -1,9 +1,15 @@ -simulation_type = 1 +collision_cfl_factor = 1e-3 cfl_factor = 0.5 max_adaptive_speedup = 0 flavor_cfl_factor = .5 maxError = 1e-6 +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1 + +perturbation_type = 0 +perturbation_amplitude = 0 + integration.type = 1 integration.rk.type = 4 @@ -15,7 +21,7 @@ Lz = 1e7 # Number of particles per cell nppc = (1, 1, 1) -nphi_equator = 1 +particle_data_filename = "particle_input.dat" # Maximum size of each grid in the domain max_grid_size = 64 @@ -76,3 +82,24 @@ alpha2_degrees = 0 # CP-violating phase in degrees [NO:222 IO:285] deltaCP_degrees = 222 + +################# +# opacity stuff # +################# +IMFP_method = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_coll_equi_test b/sample_inputs/inputs_coll_equi_test new file mode 100644 index 00000000..b2d603d3 --- /dev/null +++ b/sample_inputs/inputs_coll_equi_test @@ -0,0 +1,129 @@ +perturbation_type = 0 +perturbation_amplitude = 1e-6 + +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 0 + +collision_cfl_factor = 0.04 +cfl_factor = 1e10 +flavor_cfl_factor = 0.5 +max_adaptive_speedup = 0 +maxError = 1e-6 + +integration.type = 1 +integration.rk.type = 4 + +# Domain size in 3D index space +ncell = (2, 2, 2) +Lx = 20 +Ly = 20 +Lz = 20 + +# Number of particles per cell +nppc = (1, 1, 1) +particle_data_filename = "particle_input.dat" + +# Maximum size of each grid in the domain +max_grid_size = 16 + +# Number of steps to run +nsteps = 1000 + +# Simulation end time +end_time = 5.0e9 + +# Make FPE signal errors so we get a Backtrace +amrex.fpe_trap_invalid=1 + +# give background fluid conditions +rho_g_ccm = 0 +T_MeV = 10 +Ye = 1 + +# Write plotfiles +write_plot_every = 1000 + +# Write particle data in plotfiles +write_plot_particles_every = 1000 + +# checkpointing +do_restart = 0 +restart_dir = "" + +############################### +# NEUTRINO PHYSICS PARAMETERS # +############################### +# see first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf + +# mass state 1 mass in eV [NO/IO:-sqrt(7.39e-5)] +mass1_eV = 0 #-0.008596511 + +# mass state 2 mass in eV (define at 0 arbitrarily because oscillations only sensitive to deltaM^2) +mass2_eV = 0 + +# mass state 3 mass in eV [NO:sqrt(2.449e-3) IO:-sqrt(2.509e-3)] +mass3_eV = 0.049487372 + +# 1-2 mixing angle in degrees [NO/IO:33.82] +theta12_degrees = 1e-6 + +# 2-3 mixing angle in degrees [NO:8.61 IO:8.65] +theta23_degrees = 8.61 + +# 1-3 mixing angle in degrees [NO:48.3 IO:48.6] +theta13_degrees = 48.3 + +# Majorana angle 1 in degrees +alpha1_degrees = 0 + +# Majorana angle 2 in degrees +alpha2_degrees = 0 + +# CP-violating phase in degrees [NO:222 IO:285] +deltaCP_degrees = 222 + +################# +# opacity stuff # +################# +IMFP_method = 1 + +Do_Pauli_blocking = 0 # If 1, it will multiply the inverse mean free path by 1 / (1 - f_eq); if 0, do nothing. + +IMFP_abs0_cm = 5e-4 +IMFP_abs1_cm = 5e-4 +IMFP_abs2_cm = 5e-4 +IMFP_abs0bar_cm = 5e-4 +IMFP_abs1bar_cm = 5e-4 +IMFP_abs2bar_cm = 5e-4 + +munu0_MeV = 0 +munu1_MeV = 0 +munu2_MeV = 0 +munu0bar_MeV = 0 +munu1bar_MeV = 0 +munu2bar_MeV = 0 + +IMFP_scat0_cm = 0 +IMFP_scat1_cm = 0 +IMFP_scat2_cm = 0 +IMFP_scat0bar_cm = 0 +IMFP_scat1bar_cm = 0 +IMFP_scat2bar_cm = 0 + +delta_E = 0.8339001570751987 # Mev + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_collisional_instability_test b/sample_inputs/inputs_collisional_instability_test new file mode 100644 index 00000000..cc56120e --- /dev/null +++ b/sample_inputs/inputs_collisional_instability_test @@ -0,0 +1,129 @@ +perturbation_type = 0 +perturbation_amplitude = 0.0 + +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1.0 + +collision_cfl_factor = 1e-3 +cfl_factor = 0.5 +flavor_cfl_factor = 0.5 +max_adaptive_speedup = 0 +maxError = 1e-6 + +integration.type = 1 +integration.rk.type = 4 + +# Domain size in 3D index space +ncell = (1, 1, 1) +Lx = 1 # cm +Ly = 1 # cm +Lz = 1 # cm + +# Number of particles per cell +nppc = (1, 1, 1) +particle_data_filename = "particle_input.dat" + +# Maximum size of each grid in the domain +max_grid_size = 16 + +# Number of steps to run +nsteps = 40000 + +# Simulation end time +end_time = 5.0e19 + +# Make FPE signal errors so we get a Backtrace +amrex.fpe_trap_invalid=1 + +# give background fluid conditions +rho_g_ccm = 0 +T_MeV = 7.0 +Ye = 0 + +# Write plotfiles +write_plot_every = 500 + +# Write particle data in plotfiles +write_plot_particles_every = 500 + +# checkpointing +do_restart = 0 +restart_dir = "" + +############################### +# NEUTRINO PHYSICS PARAMETERS # +############################### +# see first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf + +# mass state 1 mass in eV [NO/IO:-sqrt(7.39e-5)] +mass1_eV = 0.04866 #-0.008596511 + +# mass state 2 mass in eV (define at 0 arbitrarily because oscillations only sensitive to deltaM^2) +mass2_eV = 0 + +# mass state 3 mass in eV [NO:sqrt(2.449e-3) IO:-sqrt(2.509e-3)] +mass3_eV = 0 + +# 1-2 mixing angle in degrees [NO/IO:33.82] +theta12_degrees = 1e-6 + +# 2-3 mixing angle in degrees [NO:8.61 IO:8.65] +theta23_degrees = 0 + +# 1-3 mixing angle in degrees [NO:48.3 IO:48.6] +theta13_degrees = 0 + +# Majorana angle 1 in degrees +alpha1_degrees = 0 + +# Majorana angle 2 in degrees +alpha2_degrees = 0 + +# CP-violating phase in degrees [NO:222 IO:285] +deltaCP_degrees = 0 + +################# +# opacity stuff # +################# +IMFP_method = 1 + +Do_Pauli_blocking = 0 # If 1, it will multiply the inverse mean free path by 1 / (1 - f_eq); if 0, do nothing. + +IMFP_abs0_cm = 2.398082e-1 +IMFP_abs1_cm = 0 +IMFP_abs2_cm = 0 +IMFP_abs0bar_cm = 2.29358e-2 +IMFP_abs1bar_cm = 0 +IMFP_abs2bar_cm = 0 + +munu0_MeV = 20.0 +munu1_MeV = 0 +munu2_MeV = 0 +munu0bar_MeV = 17.644694342915507 +munu1bar_MeV = 0 +munu2bar_MeV = 0 + +IMFP_scat0_cm = 0 +IMFP_scat1_cm = 0 +IMFP_scat2_cm = 0 +IMFP_scat0bar_cm = 0 +IMFP_scat1bar_cm = 0 +IMFP_scat2bar_cm = 0 + +delta_E = 2.272540842052914 # Mev + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_fast_flavor b/sample_inputs/inputs_fast_flavor index adc998b1..fdcdebe7 100644 --- a/sample_inputs/inputs_fast_flavor +++ b/sample_inputs/inputs_fast_flavor @@ -1,9 +1,15 @@ -simulation_type = 2 +collision_cfl_factor = 1e-3 cfl_factor = 0.5 flavor_cfl_factor = .5 max_adaptive_speedup = 0 maxError = 1e-6 +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1 + +perturbation_type = 0 +perturbation_amplitude = 0 + integration.type = 1 integration.rk.type = 4 @@ -15,7 +21,7 @@ Lz = 1e7 # Number of particles per cell nppc = (1, 1, 1) -nphi_equator = 1 +particle_data_filename = "particle_input.dat" # Maximum size of each grid in the domain max_grid_size = 64 @@ -76,3 +82,24 @@ alpha2_degrees = 0 # CP-violating phase in degrees [NO:222 IO:285] deltaCP_degrees = 222 + +################# +# opacity stuff # +################# +IMFP_method = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_fast_flavor_nonzerok b/sample_inputs/inputs_fast_flavor_nonzerok index 32ccb2ae..a20c6f00 100644 --- a/sample_inputs/inputs_fast_flavor_nonzerok +++ b/sample_inputs/inputs_fast_flavor_nonzerok @@ -1,7 +1,11 @@ -simulation_type = 3 -st3_amplitude = 1e-6 -st3_wavelength_fraction_of_domain = 1 +perturbation_type = 1 +perturbation_amplitude = 1e-6 +perturbation_wavelength_cm = 1 +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1 + +collision_cfl_factor = 1e-3 cfl_factor = 0.5 flavor_cfl_factor = 0.5 max_adaptive_speedup = 0 @@ -18,10 +22,10 @@ Lz = 1.0 # Number of particles per cell nppc = (1, 1, 1) -nphi_equator = 1 +particle_data_filename = "particle_input.dat" # Maximum size of each grid in the domain -max_grid_size = 1000 +max_grid_size = 10 # Number of steps to run nsteps = 5000 @@ -79,3 +83,23 @@ alpha2_degrees = 0 # CP-violating phase in degrees [NO:222 IO:285] deltaCP_degrees = 222 +################# +# opacity stuff # +################# +IMFP_method = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/sample_inputs/inputs_fermi_dirac_test b/sample_inputs/inputs_fermi_dirac_test new file mode 100644 index 00000000..f980f5f6 --- /dev/null +++ b/sample_inputs/inputs_fermi_dirac_test @@ -0,0 +1,108 @@ +perturbation_type = 0 +perturbation_amplitude = 1e-6 + +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 0 + +collision_cfl_factor = 100 +cfl_factor = 100 +flavor_cfl_factor = 100 +max_adaptive_speedup = 0 +maxError = 1e-6 + +integration.type = 1 +integration.rk.type = 4 + +# Domain size in 3D index space +ncell = (2,2,2) +Lx = 2.0e4 # cm +Ly = 2.0e4 # cm +Lz = 2.0e4 # cm + +# Number of particles per cell +nppc = (1, 1, 1) +particle_data_filename = "particle_input.dat" + +# Maximum size of each grid in the domain +max_grid_size = 16 + +# Number of steps to run +nsteps = 2000 + +# Simulation end time +end_time = 5.0e9 + +# Make FPE signal errors so we get a Backtrace +amrex.fpe_trap_invalid=1 + +# Write plotfiles +write_plot_every = 2000 + +# Write particle data in plotfiles +write_plot_particles_every = 2000 + +# checkpointing +do_restart = 0 +restart_dir = "" + +############################### +# NEUTRINO PHYSICS PARAMETERS # +############################### +# see first column of table 14.7 in http://pdg.lbl.gov/2019/reviews/rpp2019-rev-neutrino-mixing.pdf + +# mass state 1 mass in eV [NO/IO:-sqrt(7.39e-5)] +mass1_eV = 0 #-0.008596511 + +# mass state 2 mass in eV (define at 0 arbitrarily because oscillations only sensitive to deltaM^2) +mass2_eV = 0 + +# mass state 3 mass in eV [NO:sqrt(2.449e-3) IO:-sqrt(2.509e-3)] +mass3_eV = 0.049487372 + +# 1-2 mixing angle in degrees [NO/IO:33.82] +theta12_degrees = 1e-6 + +# 2-3 mixing angle in degrees [NO:8.61 IO:8.65] +theta23_degrees = 8.61 + +# 1-3 mixing angle in degrees [NO:48.3 IO:48.6] +theta13_degrees = 48.3 + +# Majorana angle 1 in degrees +alpha1_degrees = 0 + +# Majorana angle 2 in degrees +alpha2_degrees = 0 + +# CP-violating phase in degrees [NO:222 IO:285] +deltaCP_degrees = 222 + +################# +# opacity stuff # +################# +IMFP_method = 2 +Do_Pauli_blocking = 0 # If 1, it will multiply the inverse mean free path by 1 / (1 - f_eq); if 0, do nothing. +boundary_condition_type = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 1 +background_rho_Ye_T_table_name = "./rho_Ye_T.hdf5" + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 + +################# +# Tables +################# +nulib_table_name = "/NuLib/NuLib_SFHo.h5" +nuceos_table_name = "/EOS/LS220_234r_136t_50y_analmu_20091212_SVNr26.h5" diff --git a/sample_inputs/inputs_msw_test b/sample_inputs/inputs_msw_test index e674a58c..8e837961 100644 --- a/sample_inputs/inputs_msw_test +++ b/sample_inputs/inputs_msw_test @@ -1,9 +1,15 @@ -simulation_type = 0 +collision_cfl_factor = 1e-3 cfl_factor = 0.5 flavor_cfl_factor = 0.1 max_adaptive_speedup = 0 maxError = 1e-6 +# attenuation parameters to time derivative of N due to hamiltonians +attenuation_hamiltonians = 1 + +perturbation_type = 0 +perturbation_amplitude = 0 + integration.type = 1 integration.rk.type = 4 @@ -15,7 +21,7 @@ Lz = 1.0 # Number of particles per cell nppc = (1, 1, 1) -nphi_equator = 1 +particle_data_filename = "particle_input.dat" # Maximum size of each grid in the domain max_grid_size = 64 @@ -76,3 +82,24 @@ alpha2_degrees = 0 # CP-violating phase in degrees [NO:222 IO:285] deltaCP_degrees = 222 + +################# +# opacity stuff # +################# +IMFP_method = 0 + +################# +# Background Ye, T and rho +################# +read_rho_T_Ye_from_table = 0 + +################# +# Blackhole +################# +do_blackhole = 0 + +################# +# Boundary conditions +################# +boundary_condition_type = 0 +do_periodic_empty_bc = 0 \ No newline at end of file diff --git a/submodules/HighFive b/submodules/HighFive new file mode 160000 index 00000000..4d29deeb --- /dev/null +++ b/submodules/HighFive @@ -0,0 +1 @@ +Subproject commit 4d29deebf939ba47f8f28f0594b813fd9289c0fd diff --git a/submodules/amrex b/submodules/amrex new file mode 160000 index 00000000..b2052f2d --- /dev/null +++ b/submodules/amrex @@ -0,0 +1 @@ +Subproject commit b2052f2d2e5ce44317450ba13de705a3e01ef0ea