diff --git a/.clang-tidy b/.clang-tidy index e5635ab5b..4771bae80 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,6 +14,7 @@ Checks: > -llvmlibc-*, -cert-err33-c, -cert-err34-c, + -cert-err58-cpp, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-no-malloc, @@ -21,6 +22,7 @@ Checks: > -cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-vararg, -clang-diagnostic-error, -fuchsia-*, @@ -32,6 +34,7 @@ Checks: > -hicpp-signed-bitwise, -misc-use-anonymous-namespace, -misc-no-recursion, + -misc-non-private-member-variables-in-classes, -readability-identifier-length, -readability-function-cognitive-complexity, -readability-magic-numbers, @@ -46,7 +49,7 @@ CheckOptions: - key: cppcoreguidelines-init-variables.MathHeader value: - key: cppcoreguidelines-narrowing-conversions.IgnoreConversionFromTypes - value: size_t;ptrdiff_t;size_type;difference_type;time_t + value: size_t;ptrdiff_t;size_type;difference_type;time_t;MPI_Aint;unsigned long - key: cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion value: 'false' - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion diff --git a/.github/workflows/ci-checks.yml b/.github/workflows/ci-checks.yml index 1f3ee5103..9041d052f 100644 --- a/.github/workflows/ci-checks.yml +++ b/.github/workflows/ci-checks.yml @@ -11,23 +11,26 @@ on: jobs: cppcheck: - runs-on: ubuntu-22.04 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: install dependencies run: | - # git status - # sudo apt-get update - # sudo apt-get install libgsl-dev - # sudo apt install -y openmpi-bin libopenmpi-dev - sudo apt-get -y install cppcheck + brew update + brew install gsl openmpi cppcheck + cp artisoptions_nltenebular.h artisoptions.h - - name: run cppcheck + - name: run cppcheck and check for errors run: | - cp artisoptions_nltenebular.h artisoptions.h - cppcheck --force --language=c++ --std=c++20 . + cppcheck --version + cppcheck --force --error-exitcode=1 --language=c++ --std=c++20 --enable=warning,performance,portability . + + - name: show cppcheck style suggestions + run: | + cppcheck --version + cppcheck --force --language=c++ --std=c++20 --enable=style --suppress=knownConditionTrueFalse . clang-format: runs-on: ubuntu-22.04 @@ -45,12 +48,23 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - compiler: [{name: gcc, ver: 11}, {name: gcc, ver: 12}, {name: clang, ver: 14}, {name: clang, ver: 15}] + compiler: [{name: gcc, ver: 11}, {name: gcc, ver: 12}, {name: gcc, ver: 13}, {name: clang, ver: 14}, {name: clang, ver: 15}] mpi: [ON, OFF] + openmp: [ON, OFF] artisoptionsfile: [artisoptions_classic.h, artisoptions_nltenebular.h] + exclude: + - compiler: {name: gcc, ver: 11} + openmp: ON + - compiler: {name: gcc, ver: 12} + openmp: ON + - compiler: {name: gcc, ver: 13} + openmp: ON + - compiler: {name: clang, ver: 15} + openmp: ON fail-fast: false - name: ${{ matrix.compiler.name }}-${{ matrix.compiler.ver }}${{ matrix.mpi == 'ON' && ' MPI' || ''}} ${{ matrix.artisoptionsfile }} + name: ${{ matrix.compiler.name }}-${{ matrix.compiler.ver }}${{ matrix.mpi == 'ON' && ' MPI' || ''}}${{ matrix.openmp == 'ON' && ' OpenMP' || ''}} + ${{ matrix.artisoptionsfile }} steps: - uses: actions/checkout@v3 @@ -61,20 +75,24 @@ jobs: # echo "count=$(python3 -c 'import psutil; print(int(psutil.cpu_count(logical=False)))')" >> $GITHUB_OUTPUT echo "count=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')" >> $GITHUB_OUTPUT - - name: apt-get update + - name: apt update run: | + sudo add-apt-repository main + sudo add-apt-repository universe + sudo add-apt-repository restricted + sudo add-apt-repository multiverse sudo apt-get update - name: Install gcc-${{ matrix.compiler.ver }} if: matrix.compiler.name == 'gcc' run: | - sudo apt-get install -y gcc-${{ matrix.compiler.ver }} g++-${{ matrix.compiler.ver }} + sudo apt install -y gcc-${{ matrix.compiler.ver }} g++-${{ matrix.compiler.ver }} echo "CXX=g++-${{ matrix.compiler.ver }}" >> $GITHUB_ENV - name: Install clang-${{ matrix.compiler.ver }} if: matrix.compiler.name == 'clang' run: | - sudo apt-get install -y clang-${{ matrix.compiler.ver }} --install-suggests + sudo apt install -y clang-${{ matrix.compiler.ver }} --install-suggests echo "CXX=clang++-${{ matrix.compiler.ver }}" >> $GITHUB_ENV - name: install openmpi @@ -82,8 +100,13 @@ jobs: run: | sudo apt install -y openmpi-bin libopenmpi-dev + - name: install OpenMP + if: matrix.openmp == 'ON' + run: | + sudo apt-get install -y libomp5-14 libomp-dev + - name: install gsl - run: sudo apt-get install libgsl-dev + run: sudo apt install libgsl-dev # - name: Set compiler environment variables (MPI off) # if: matrix.mpi == 'OFF' @@ -103,4 +126,4 @@ jobs: - name: Compile run: | cp -v -p ${{ matrix.artisoptionsfile }} artisoptions.h - make MPI=${{matrix.mpi}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec + make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38eb691a2..71dae98ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: # os: ['ubuntu-latest', 'self-hosted'] os: [ubuntu-22.04] testmode: [OFF, ON] - testname: [classicmode, classicmode_3d, kilonova, nebularonezone] + testname: [classicmode_1d_3dgrid, classicmode_3d, kilonova_1d_1dgrid, kilonova_1d_3dgrid, kilonova_2d_2dgrid, kilonova_2d_3dgrid, nebularonezone_1d_3dgrid] exclude: - os: self-hosted testmode: ON @@ -27,7 +27,7 @@ jobs: runs-on: ${{ matrix.os }} timeout-minutes: 45 - name: ${{ matrix.testname }} ${{ matrix.testmode == 'ON' && ' testmode ON' || ''}} + name: ${{ matrix.testname }}${{ matrix.testmode == 'ON' && ' testmode ON' || ''}} steps: - uses: actions/checkout@v3 @@ -38,11 +38,11 @@ jobs: if: matrix.os != 'selfhosted' run: | git status - sudo apt-get update - sudo apt-get install -y gcc-12 g++-12 - sudo apt-get install -y libgsl-dev + sudo apt update + sudo apt install -y gcc-13 g++-13 + sudo apt install -y libgsl-dev sudo apt install -y openmpi-bin libopenmpi-dev - echo "OMPI_CXX=g++-12" >> $GITHUB_ENV + echo "OMPI_CXX=g++-13" >> $GITHUB_ENV - name: CPU type and core count id: cpu-count @@ -52,8 +52,7 @@ jobs: # cache this for classic options because the super low integration tolerance makes generation of the file very slow - name: Cache ratecoeff.dat - if: matrix.testname == 'classicmode' || matrix.testname == 'classicmode_3d' - # if: matrix.testname == 'classicmode' && matrix.testmode != 'ON' + if: matrix.testname == 'classicmode_1d_3dgrid' || matrix.testname == 'classicmode_3d' uses: actions/cache@v3 with: path: tests/${{ matrix.testname }}_testrun/ratecoeff.dat @@ -62,7 +61,7 @@ jobs: tests/${{ matrix.testname }}_testrun/ratecoeff.dat- - name: Cache test atomic data - if: matrix.testname != 'classicmode' + if: ${{ !startsWith(matrix.testname, 'classicmode_1d') }} uses: actions/cache@v3 id: cache-testatomicdata with: @@ -70,7 +69,7 @@ jobs: key: tests/atomicdata_feconi.tar.xz - name: Cache test atomic data classic - if: matrix.testname == 'classicmode' + if: ${{ startsWith(matrix.testname, 'classicmode_1d') }} uses: actions/cache@v3 id: cache-testatomicdata-classic with: @@ -124,8 +123,7 @@ jobs: if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' working-directory: tests/${{ matrix.testname }}_testrun run: | - md5sum *.out job0/*.out - + md5sum *.out job0/*.out | tee ../${{ matrix.testname }}_inputfiles/results_md5_job0.txt if [ -f results_md5_job0.txt ]; then md5sum -c results_md5_job0.txt; else echo "results_md5_job0.txt not found"; fi - name: Run test job1 resume @@ -152,6 +150,11 @@ jobs: working-directory: tests/${{ matrix.testname }}_testrun/ run: cat job1/output_0-0.txt + - name: cat job1 deposition.out + if: always() + working-directory: tests/${{ matrix.testname }}_testrun/ + run: cat deposition.out + - name: Run exspec if: always() working-directory: tests/${{ matrix.testname }}_testrun/ @@ -174,7 +177,7 @@ jobs: if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' working-directory: tests/${{ matrix.testname }}_testrun run: | - md5sum *.out job1/*.out + md5sum *.out job1/*.out | tee ../${{ matrix.testname }}_inputfiles/results_md5_final.txt if [ -f results_md5_final.txt ]; then md5sum -c results_md5_final.txt; else echo "results_md5_final.txt not found"; fi - name: Prepare for next steps @@ -192,6 +195,13 @@ jobs: name: test-${{ matrix.testname }}-output path: tests/${{ matrix.testname }}_testrun/output + - name: Upload checksum files + uses: actions/upload-artifact@v3 + if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' + with: + name: ${{ matrix.testname }}_inputfiles + path: tests/${{ matrix.testname }}_inputfiles/results_md5* + - name: Set up Python if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' uses: actions/setup-python@v4 @@ -252,3 +262,21 @@ jobs: # cp input-newrun.txt input.txt # touch output_0-0.txt # time mpirun -np 2 ./sn3d + + combine_checksums: + needs: testmodels + if: always() + runs-on: ubuntu-latest + steps: + - name: Download test output + uses: actions/download-artifact@v3 + + - name: List all files + if: always() + run: find . + + - name: Upload bundled checksum files + uses: actions/upload-artifact@v3 + with: + name: checksums + path: '*_inputfiles/results_md5*.txt' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 190b96429..1bfa2da8f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files args: [--maxkb=800] diff --git a/CMakeLists.txt b/CMakeLists.txt index 588c87c55..ee4ff501f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.26) project(artis) set(default_build_type "Release") @@ -11,16 +11,18 @@ set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}) include_directories("${PROJECT_SOURCE_DIR}") set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +#set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) -set(SN3D_SOURCES sn3d.cc atomic.cc boundary.cc emissivities.cc gamma.cc globals.cc grey_emissivities.cc grid.cc input.cc kpkt.cc light_curve.cc ltepop.cc macroatom.cc nltepop.cc nonthermal.cc decay.cc packets.cc photo_electric.cc polarization.cc radfield.cc ratecoeff.cc rpkt.cc stats.cc thermalbalance.cc update_grid.cc update_packets.cc vectors.cc vpkt.cc md5.cc +set(SN3D_SOURCES sn3d.cc atomic.cc boundary.cc gammapkt.cc globals.cc grid.cc input.cc kpkt.cc light_curve.cc ltepop.cc macroatom.cc nltepop.cc nonthermal.cc decay.cc packet.cc radfield.cc ratecoeff.cc rpkt.cc spectrum.cc stats.cc thermalbalance.cc update_grid.cc update_packets.cc vectors.cc vpkt.cc md5.cc) add_executable(sn3d ${SN3D_SOURCES}) -set(EXSPEC_SOURCES exspec.cc grid.cc globals.cc input.cc vectors.cc packets.cc update_grid.cc update_packets.cc gamma.cc boundary.cc macroatom.cc decay.cc rpkt.cc kpkt.cc photo_electric.cc emissivities.cc grey_emissivities.cc ltepop.cc atomic.cc ratecoeff.cc thermalbalance.cc light_curve.cc spectrum.cc polarization.cc nltepop.cc nonthermal.cc radfield.cc stats.cc vpkt.cc md5.cc) +set(EXSPEC_SOURCES exspec.cc grid.cc globals.cc input.cc vectors.cc packet.cc update_grid.cc update_packets.cc gammapkt.cc boundary.cc macroatom.cc decay.cc rpkt.cc kpkt.cc ltepop.cc atomic.cc ratecoeff.cc thermalbalance.cc light_curve.cc spectrum.cc nltepop.cc nonthermal.cc radfield.cc stats.cc vpkt.cc md5.cc) add_executable(exspec ${EXSPEC_SOURCES}) +#if(UNIX AND NOT APPLE) if(UNIX AND NOT APPLE) find_package(MPI) include_directories(SYSTEM ${MPI_INCLUDE_PATH}) @@ -32,7 +34,7 @@ add_compile_options("-Wall" "-Wextra") string(APPEND CMAKE_CXX_FLAGS_DEBUG "-g") string(APPEND CMAKE_CXX_FLAGS_RELEASE "-O3") -set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -march=native") +#set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -march=native") add_compile_definitions(HAVE_INLINE) add_compile_definitions(GSL_RANGE_CHECK_OFF) diff --git a/Makefile b/Makefile index 45a5a3905..0955d5017 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,8 @@ # place in architecture folder, e.g. build/arm64 BUILD_DIR = build/$(shell uname -m) -CXXFLAGS += -std=c++20 -fstrict-aliasing -ftree-vectorize -g -flto=auto -Werror -Werror=undef -# CXXFLAGS += -Wpedantic -Wextra -Wall +CXXFLAGS += -std=c++20 -fstrict-aliasing -ftree-vectorize -flto=auto + # CXXFLAGS += -Wunreachable-code ifeq ($(shell uname -s),Darwin) @@ -18,6 +18,7 @@ ifeq ($(shell uname -s),Darwin) CXXFLAGS += -march=native endif + CXXFLAGS += -fno-omit-frame-pointer # CXXFLAGS += -Rpass=loop-vectorize # CXXFLAGS += -Rpass-missed=loop-vectorize # CXXFLAGS += -Rpass-analysis=loop-vectorize @@ -77,20 +78,24 @@ ifeq ($(TESTMODE),ON) CXXFLAGS += -DTESTMODE=true -O3 -DLIBCXX_ENABLE_DEBUG_MODE # makes GitHub actions classic test run forever? # CXXFLAGS += -D_GLIBCXX_DEBUG - CXXFLAGS += -fsanitize=address -fno-omit-frame-pointer -fno-common + CXXFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -fno-common BUILD_DIR := $(BUILD_DIR)_testmode else # skip array range checking for better performance and use optimizations CXXFLAGS += -DTESTMODE=false -DGSL_RANGE_CHECK_OFF -O3 endif -CXXFLAGS += -Winline -Wall -Wpedantic -Wredundant-decls -Wundef -Wno-unused-parameter -Wno-unused-function -Wstrict-aliasing -Wno-inline +CXXFLAGS += -Werror -Werror=undef -Winline -Wall -Wpedantic -Wredundant-decls -Wundef -Wno-unused-parameter -Wno-unused-function -Wstrict-aliasing -Wno-inline -ifeq ($(MPI),ON) -else ifeq ($(MPI),OFF) -else ifeq ($(MPI),) +ifeq ($(MPI),) # MPI option not specified. set to true by default MPI := ON +endif +ifeq ($(MPI),ON) + CXX = mpicxx + CXXFLAGS += -DMPI_ON=true + BUILD_DIR := $(BUILD_DIR)_mpi +else ifeq ($(MPI),OFF) else $(error bad value for MPI option. Should be ON or OFF) endif @@ -102,17 +107,14 @@ else $(error bad value for testmode option. Should be ON or OFF) endif -ifeq ($(MPI),ON) - CXX = mpicxx - CXXFLAGS += -DMPI_ON=true - BUILD_DIR := $(BUILD_DIR)_mpi -endif - ifeq ($(OPENMP),ON) - CXXFLAGS += -Xpreprocessor - CXXFLAGS += -fopenmp + CXXFLAGS += -Xpreprocessor -fopenmp LDFLAGS += -lomp BUILD_DIR := $(BUILD_DIR)_openmp +else ifeq ($(OPENMP),OFF) +else ifeq ($(OPENMP),) +else +$(error bad value for testmode option. Should be ON or OFF) endif ### use pg when you want to use gprof profiler @@ -137,7 +139,7 @@ sn3d: $(sn3d_objects) -include $(sn3d_dep) sn3dwhole: version.h - $(CXX) $(CXXFLAGS) $(sn3d_files) $(LDFLAGS) -o sn3d + $(CXX) $(CXXFLAGS) -g $(sn3d_files) $(LDFLAGS) -o sn3d $(BUILD_DIR)/%.o: %.cc artisoptions.h Makefile @mkdir -p $(@D) diff --git a/README.old b/README.old index fb9f44cda..bcfeb17ae 100644 --- a/README.old +++ b/README.old @@ -208,7 +208,7 @@ code until 2012-07-30: 2009-07-15, v108 - Modified initial grey approximation now calculates an optical depth to the surface of the simulation - volume using kappa_grey. + volume using chi_grey. - As of now (2009-07-15) the local thomson and grey optical depth in a cell and the depth to the surface are save in the estimator files. This requires @@ -226,7 +226,7 @@ code until 2012-07-30: files. - Proper initialisation of absorption counters added (2009-05-30). - Changes to the initial grey approximation, reintroducing - the concept of kappa_grey. + the concept of chi_grey. - Array boundaries which have to be changed for each run moved to types.h diff --git a/artisoptions_christinenonthermal.h b/artisoptions_christinenonthermal.h index 3f1fed21a..9c5cf6ead 100644 --- a/artisoptions_christinenonthermal.h +++ b/artisoptions_christinenonthermal.h @@ -8,14 +8,11 @@ constexpr int MPKTS = 10000; -constexpr int GRID_TYPE = GRID_UNIFORM; +constexpr int GRID_TYPE = GRID_CARTESIAN3D; constexpr int CUBOID_NCOORDGRID_X = 100; constexpr int CUBOID_NCOORDGRID_Y = 100; constexpr int CUBOID_NCOORDGRID_Z = 100; - -constexpr bool NLTE_POPS_ON = true; - -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS = true; +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE = false; constexpr int NLTEITER = 30; @@ -28,6 +25,8 @@ constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { constexpr bool LTEPOP_EXCITATION_USE_TJ = false; +constexpr bool FORCE_SAHA_ION_BALANCE(int element_z) { return false; } + constexpr bool single_level_top_ion = false; constexpr bool single_ground_level = false; @@ -139,8 +138,6 @@ constexpr double FIXED_TIMESTEP_WIDTH = -1.; constexpr double TIMESTEP_TRANSITION_TIME = -1.; -constexpr bool USE_GSL_RANDOM = true; - constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; diff --git a/artisoptions_classic.h b/artisoptions_classic.h index d27685f85..08eb7f280 100644 --- a/artisoptions_classic.h +++ b/artisoptions_classic.h @@ -8,14 +8,11 @@ constexpr int MPKTS = 100000; -constexpr int GRID_TYPE = GRID_UNIFORM; +constexpr int GRID_TYPE = GRID_CARTESIAN3D; constexpr int CUBOID_NCOORDGRID_X = 100; constexpr int CUBOID_NCOORDGRID_Y = 100; constexpr int CUBOID_NCOORDGRID_Z = 100; - -constexpr bool NLTE_POPS_ON = false; - -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS = false; +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE = false; constexpr int NLTEITER = 30; @@ -23,6 +20,8 @@ constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { return fa constexpr bool LTEPOP_EXCITATION_USE_TJ = true; +constexpr bool FORCE_SAHA_ION_BALANCE(int element_z) { return false; } + constexpr bool single_level_top_ion = true; constexpr bool single_ground_level = true; @@ -135,8 +134,6 @@ constexpr double FIXED_TIMESTEP_WIDTH = -1.; constexpr double TIMESTEP_TRANSITION_TIME = -1.; -constexpr bool USE_GSL_RANDOM = true; - constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; diff --git a/artisoptions_doc.md b/artisoptions_doc.md index ba5da3d66..a3f56f342 100644 --- a/artisoptions_doc.md +++ b/artisoptions_doc.md @@ -2,26 +2,26 @@ // Number of energy packets per process (MPI rank). OpenMP threads share these packets constexpr int MPKTS; -constexpr int GRID_TYPE = {GRID_UNIFORM, GRID_SPHERICAL1D} +constexpr int GRID_TYPE = {GRID_CARTESIAN3D, GRID_CYLINDRICAL2D, GRID_SPHERICAL1D} + +// for GRID_CARTESIAN3D, set the dimensions. This will have no effect with a 3D model.txt since they will be set to match the input constexpr int CUBOID_NCOORDGRID_X; constexpr int CUBOID_NCOORDGRID_Y; constexpr int CUBOID_NCOORDGRID_Z; -// non-LTE population solver -constexpr bool NLTE_POPS_ON; - -// solve the NLTE population matrix equation simultaneously for levels of all ions of an element -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS; +// for 2D cylindrical and 3D Cartesian, remove the corners (v > vmax) to force a spherical escape surface +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE; // maximum number of NLTE/Te/Spencer-Fano iterations constexpr int NLTEITER; -// this macro function determines which levels of which ions will be treated in full NLTE (if NLTE_POPS_ON is true) +// this macro function determines which levels of which ions will be treated in full NLTE // for now, all NLTE levels should be contiguous and include the ground state // (i.e. level indices < X should return true for some X) constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { return false; } // Use TJ radiation density temperature for Boltzmann excitation formua instead of electron temperature Te +// This is default on for classic, and off for nebularnlte, where it affects the super-level constexpr bool LTEPOP_EXCITATION_USE_TJ = false; // Only include a single level for the highest ion stage @@ -63,6 +63,8 @@ constexpr double RECOMBCALIBRATION_T_ELEC; // Polarisation for real packets constexpr bool DIPOLE; + +// Only affects exspec and enables writing specpol.out, emissionpol.out, absorptionpol.out constexpr bool POL_ON; // Polarisation for virtual packets @@ -212,8 +214,6 @@ constexpr double FIXED_TIMESTEP_WIDTH; constexpr double TIMESTEP_TRANSITION_TIME; -constexpr bool USE_GSL_RANDOM; - // once a new gridsave and packets*.tmp have been written, don't delete the previous set constexpr bool KEEP_ALL_RESTART_FILES; diff --git a/artisoptions_kilonova_lte.h b/artisoptions_kilonova_lte.h index df1a277d4..02b8000fc 100644 --- a/artisoptions_kilonova_lte.h +++ b/artisoptions_kilonova_lte.h @@ -8,20 +8,19 @@ constexpr int MPKTS = 15000; -constexpr int GRID_TYPE = GRID_UNIFORM; +constexpr int GRID_TYPE = GRID_CARTESIAN3D; constexpr int CUBOID_NCOORDGRID_X = 50; constexpr int CUBOID_NCOORDGRID_Y = 50; constexpr int CUBOID_NCOORDGRID_Z = 50; - -constexpr bool NLTE_POPS_ON = false; - -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS = true; +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE = false; constexpr int NLTEITER = 30; constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { return false; } -constexpr bool LTEPOP_EXCITATION_USE_TJ = false; +constexpr bool LTEPOP_EXCITATION_USE_TJ = true; + +constexpr bool FORCE_SAHA_ION_BALANCE(int element_z) { return false; } constexpr bool single_level_top_ion = false; @@ -134,8 +133,6 @@ constexpr double FIXED_TIMESTEP_WIDTH = -1.; constexpr double TIMESTEP_TRANSITION_TIME = -1.; -constexpr bool USE_GSL_RANDOM = true; - constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; diff --git a/artisoptions_nltenebular.h b/artisoptions_nltenebular.h index 55689fcab..4de2507d9 100644 --- a/artisoptions_nltenebular.h +++ b/artisoptions_nltenebular.h @@ -8,14 +8,11 @@ constexpr int MPKTS = 1000000; -constexpr int GRID_TYPE = GRID_UNIFORM; +constexpr int GRID_TYPE = GRID_CARTESIAN3D; constexpr int CUBOID_NCOORDGRID_X = 50; constexpr int CUBOID_NCOORDGRID_Y = 50; constexpr int CUBOID_NCOORDGRID_Z = 50; - -constexpr bool NLTE_POPS_ON = true; - -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS = true; +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE = false; constexpr int NLTEITER = 30; @@ -28,6 +25,8 @@ constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { constexpr bool LTEPOP_EXCITATION_USE_TJ = false; +constexpr bool FORCE_SAHA_ION_BALANCE(int element_z) { return false; } + constexpr bool single_level_top_ion = false; constexpr bool single_ground_level = false; @@ -142,8 +141,6 @@ constexpr double FIXED_TIMESTEP_WIDTH = -1.; constexpr double TIMESTEP_TRANSITION_TIME = -1.; -constexpr bool USE_GSL_RANDOM = true; - constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; diff --git a/artisoptions_nltewithoutnonthermal.h b/artisoptions_nltewithoutnonthermal.h index e83015c95..d42344dcb 100644 --- a/artisoptions_nltewithoutnonthermal.h +++ b/artisoptions_nltewithoutnonthermal.h @@ -8,14 +8,11 @@ constexpr int MPKTS = 10000; -constexpr int GRID_TYPE = GRID_UNIFORM; +constexpr int GRID_TYPE = GRID_CARTESIAN3D; constexpr int CUBOID_NCOORDGRID_X = 100; constexpr int CUBOID_NCOORDGRID_Y = 100; constexpr int CUBOID_NCOORDGRID_Z = 100; - -constexpr bool NLTE_POPS_ON = true; - -constexpr bool NLTE_POPS_ALL_IONS_SIMULTANEOUS = true; +constexpr bool FORCE_SPHERICAL_ESCAPE_SURFACE = false; constexpr int NLTEITER = 30; @@ -28,6 +25,8 @@ constexpr bool LEVEL_IS_NLTE(int element_z, int ionstage, int level) { constexpr bool LTEPOP_EXCITATION_USE_TJ = false; +constexpr bool FORCE_SAHA_ION_BALANCE(int element_z) { return false; } + constexpr bool single_level_top_ion = true; constexpr bool single_ground_level = true; @@ -138,8 +137,6 @@ constexpr double FIXED_TIMESTEP_WIDTH = 0.1; constexpr double TIMESTEP_TRANSITION_TIME = 5; -constexpr bool USE_GSL_RANDOM = true; - constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = true; diff --git a/atomic.cc b/atomic.cc index 7e995d3d6..693ad4746 100644 --- a/atomic.cc +++ b/atomic.cc @@ -1,6 +1,7 @@ #include "atomic.h" #include +#include #include "artisoptions.h" #include "grid.h" @@ -9,7 +10,6 @@ double last_phixs_nuovernuedge = -1; // last photoion cross section point as a factor of nu_edge = last_phixs_nuovernuedge -int nelements = 0; // total number of elements included in the simulation int maxnions = 0; // highest number of ions for any element int includedions = 0; // number of ions of any element std::array phixs_file_version_exists; @@ -57,8 +57,8 @@ auto get_tau_sobolev(const int modelgridindex, const int lineindex, const double return tau_sobolev; } -auto get_nntot(int modelgridindex) -> double -// total ion (nuclei) density +auto get_nnion_tot(int modelgridindex) -> double +// total density of nuclei { double nntot = 0.; for (int element = 0; element < get_nelements(); element++) { @@ -73,9 +73,6 @@ auto is_nlte(const int element, const int ion, const int level) -> bool // (note this function returns true for the ground state, // although it is stored separately from the excited NLTE states) { - if (!NLTE_POPS_ON) { - return false; - } return LEVEL_IS_NLTE(get_atomicnumber(element), get_ionstage(element, ion), level); // defined in artisoptions.h } @@ -83,7 +80,7 @@ auto is_nlte(const int element, const int ion, const int level) -> bool auto level_isinsuperlevel(const int element, const int ion, const int level) -> bool // ion has NLTE levels, but this one is not NLTE => is in the superlevel { - return (NLTE_POPS_ON && !is_nlte(element, ion, level) && level != 0 && (get_nlevels_nlte(element, ion) > 0)); + return (!is_nlte(element, ion, level) && level != 0 && (get_nlevels_nlte(element, ion) > 0)); } auto photoionization_crosssection_fromtable(const float *const photoion_xs, const double nu_edge, const double nu) @@ -98,7 +95,7 @@ auto photoionization_crosssection_fromtable(const float *const photoion_xs, cons // return 1.; // return 1. * pow(nu_edge / nu, 3); - float sigma_bf = NAN; + float sigma_bf = 0.; if (phixs_file_version_exists[1] && !phixs_file_version_exists[2]) { // classic mode: no interpolation @@ -156,9 +153,9 @@ auto photoionization_crosssection_fromtable(const float *const photoion_xs, cons return sigma_bf; } -void set_nelements(const int nelements_in) { nelements = nelements_in; } +void set_nelements(const int nelements_in) { globals::elements.resize(nelements_in); } -auto get_nelements() -> int { return nelements; } +auto get_nelements() -> int { return globals::elements.size(); } auto get_atomicnumber(const int element) -> int /// Returns the atomic number associated with a given elementindex. @@ -173,20 +170,26 @@ auto get_elementindex(const int Z) -> int /// If there is no element with the given atomic number in the atomic data /// a negative value is returned to flag this event. { - for (int i = 0; i < get_nelements(); i++) { - // printf("i %d, Z %d, elements[i].anumber %d\n",i,Z,elements[i].anumber); - if (Z == globals::elements[i].anumber) { - return i; - } + const auto elem = + std::ranges::find_if(globals::elements, [Z](const elementlist_entry &element) { return element.anumber == Z; }); + if (elem != globals::elements.end()) { + return std::distance(globals::elements.begin(), elem); } - // printout("[debug] get_elementindex: element Z=%d was not found in atomic data ... skip readin of cross sections for - // this element\n",Z); printout("[fatal] get_elementindex: element Z=%d was not found in atomic data ... abort\n"); - // abort();; + // printout("[debug] get_elementindex: element Z=%d was not found in atomic data ... skip readin of cross sections + // for this element\n",Z); printout("[fatal] get_elementindex: element Z=%d was not found in atomic data ... + // abort\n"); abort();; return -100; } -void increase_includedions(const int nions) { includedions += nions; } +void update_includedions_maxnions() { + includedions = 0; + maxnions = 0; + for (int element = 0; element < get_nelements(); element++) { + includedions += get_nions(element); + maxnions = std::max(maxnions, get_nions(element)); + } +} auto get_includedions() -> int // returns the number of ions of all elements combined @@ -197,11 +200,7 @@ auto get_includedions() -> int void update_max_nions(const int nions) // Will ensure that maxnions is always greater than or equal to the number of nions // this is called at startup once per element with the number of ions -{ - if (nions > maxnions || maxnions < 0) { - maxnions = nions; - } -} +{} auto get_max_nions() -> int { // number greater than or equal to nions(element) for all elements @@ -234,6 +233,19 @@ auto get_nlevels(const int element, const int ion) -> int return globals::elements[element].ions[ion].nlevels; } +auto elem_has_nlte_levels(const int element) -> bool { return globals::elements[element].has_nlte_levels; } + +auto elem_has_nlte_levels_search(const int element) -> bool { + for (int ion = 0; ion < get_nions(element); ion++) { + for (int level = 1; level < get_nlevels(element, ion); level++) { + if (is_nlte(element, ion, level)) { + return true; + } + } + } + return false; +} + auto get_nlevels_nlte(const int element, const int ion) -> int // Returns the number of NLTE levels associated with with a specific ion given // its elementindex and ionindex. Includes the superlevel if there is one but does not include the ground state diff --git a/atomic.h b/atomic.h index c687e7cab..170f4e620 100644 --- a/atomic.h +++ b/atomic.h @@ -13,7 +13,7 @@ int get_continuumindex_phixstargetindex(int element, int ion, int level, int phi int get_continuumindex(int element, int ion, int level, int upperionlevel); int get_phixtargetindex(int element, int ion, int level, int upperionlevel); double get_tau_sobolev(int modelgridindex, int lineindex, double t_current); -double get_nntot(int modelgridindex); +auto get_nnion_tot(int modelgridindex) -> double; bool is_nlte(int element, int ion, int level); bool level_isinsuperlevel(int element, int ion, int level); double photoionization_crosssection_fromtable(const float *photoion_xs, double nu_edge, double nu); @@ -21,9 +21,8 @@ void set_nelements(int nelements_in); int get_nelements(); int get_atomicnumber(int element); int get_elementindex(int Z); -void increase_includedions(int nions); int get_includedions(); -void update_max_nions(int nions); +void update_includedions_maxnions(); int get_max_nions(); int get_nions(int element); int get_ionstage(int element, int ion); @@ -49,5 +48,7 @@ void set_nuptrans(int element, int ion, int level, int nuptrans); double einstein_spontaneous_emission(int lineindex); double photoionization_crosssection(int element, int ion, int level, double nu_edge, double nu); double get_phixs_threshold(int element, int ion, int level, int phixstargetindex); +bool elem_has_nlte_levels(int element); +bool elem_has_nlte_levels_search(int element); #endif // ATOMIC_H diff --git a/boundary.cc b/boundary.cc deleted file mode 100644 index 38e195d7c..000000000 --- a/boundary.cc +++ /dev/null @@ -1,372 +0,0 @@ -// #include -#include "boundary.h" - -#include - -#include "grid.h" -#include "rpkt.h" -#include "sn3d.h" -#include "stats.h" -#include "update_packets.h" -#include "vectors.h" - -static auto get_shellcrossdist(const double pos[3], const double dir[3], const double shellradius, - const bool isinnerboundary, const double tstart) -> double -// find the closest forward distance to the intersection of a ray with an expanding spherical shell -// return -1 if there are no forward intersections (or if the intersection is tangential to the shell) -{ - assert_always(shellradius > 0); - constexpr bool debug = false; - if constexpr (debug) { - printout("get_shellcrossdist isinnerboundary %d\n", isinnerboundary); - printout("shellradius %g tstart %g len(pos) %g\n", shellradius, tstart, vec_len(pos)); - } - const double speed = vec_len(dir) * CLIGHT_PROP; - const double a = dot(dir, dir) - pow(shellradius / tstart / speed, 2); - const double b = 2 * (dot(dir, pos) - pow(shellradius, 2) / tstart / speed); - const double c = dot(pos, pos) - pow(shellradius, 2); - - const double discriminant = pow(b, 2) - 4 * a * c; - - if (discriminant < 0) { - // no intersection - assert_always(shellradius < vec_len(pos)); - if constexpr (debug) { - printout("no intersection\n"); - } - return -1; - } - if (discriminant > 0) { - // two intersections - double d1 = (-b + sqrt(discriminant)) / 2 / a; - double d2 = (-b - sqrt(discriminant)) / 2 / a; - - double posfinal1[3]; - double posfinal2[3]; - - cblas_dcopy(3, pos, 1, posfinal1, 1); // posfinal1 = pos - cblas_daxpy(3, d1, dir, 1, posfinal1, 1); // posfinal1 += d1 * dir; - - cblas_dcopy(3, pos, 1, posfinal2, 1); - cblas_daxpy(3, d2, dir, 1, posfinal2, 1); - - // for (int d = 0; d < 3; d++) { - // posfinal1[d] = pos[d] + d1 * dir[d]; - // posfinal2[d] = pos[d] + d2 * dir[d]; - // } - - const double shellradiusfinal1 = shellradius / tstart * (tstart + d1 / speed); - const double shellradiusfinal2 = shellradius / tstart * (tstart + d2 / speed); - // printout("solution1 d1 %g radiusfinal1 %g shellradiusfinal1 %g\n", d1, vec_len(posfinal1), shellradiusfinal1); - // printout("solution2 d2 %g radiusfinal2 %g shellradiusfinal2 %g\n", d2, vec_len(posfinal2), shellradiusfinal2); - assert_always(fabs(vec_len(posfinal1) / shellradiusfinal1 - 1.) < 1e-3); - assert_always(fabs(vec_len(posfinal2) / shellradiusfinal2 - 1.) < 1e-3); - - // invalidate any solutions that require entering the boundary from the wrong radial direction - if (isinnerboundary) { - if (dot(posfinal1, dir) > 0.) { - d1 = -1; - } - if (dot(posfinal2, dir) > 0.) { - d2 = -1; - } - } else { - if (dot(posfinal1, dir) < 0.) { - d1 = -1; - } - if (dot(posfinal2, dir) < 0.) { - d2 = -1; - } - } - - // negative d means in the reverse direction along the ray - // ignore negative d values, and if two are positive then return the smaller one - if (d1 < 0 && d2 < 0) { - return -1; - } - if (d2 < 0) { - return d1; - } - if (d1 < 0) { - return d2; - } - return fmin(d1, d2); - - } // exactly one intersection - // ignore this and don't change which cell the packet is in - assert_always(shellradius <= vec_len(pos)); - printout("single intersection\n"); - return -1.; -} - -auto boundary_cross(struct packet *const pkt_ptr, int *snext) -> double -/// Basic routine to compute distance to a cell boundary. -{ - const double tstart = pkt_ptr->prop_time; - - // There are six possible boundary crossings. Each of the three - // cartesian coordinates may be taken in turn. For x, the packet - // trajectory is - // x = x0 + (dir.x) * c * (t - tstart) - // the boundries follow - // x+/- = x+/-(tmin) * (t/tmin) - // so the crossing occurs when - // t = (x0 - (dir.x)*c*tstart)/(x+/-(tmin)/tmin - (dir.x)c) - - // Modified so that it also returns the distance to the closest cell - // boundary, regardless of direction. - - // d is used to loop over the coordinate indicies 0,1,2 for x,y,z - - const int cellindex = pkt_ptr->where; - - // the following four vectors are in grid coordinates, so either x,y,z or r - const int ndim = grid::get_ngriddimensions(); - assert_testmodeonly(ndim <= 3); - double initpos[3] = {0}; // pkt_ptr->pos converted to grid coordinates - double cellcoordmax[3] = {0}; - double vel[3] = {0}; // pkt_ptr->dir * CLIGHT_PROP converted to grid coordinates - - if (GRID_TYPE == GRID_UNIFORM) { - // XYZ coordinates - for (int d = 0; d < ndim; d++) { - initpos[d] = pkt_ptr->pos[d]; - cellcoordmax[d] = grid::get_cellcoordmax(cellindex, d); - vel[d] = pkt_ptr->dir[d] * CLIGHT_PROP; - } - } else if (GRID_TYPE == GRID_SPHERICAL1D) { - // the only coordinate is radius from the origin - initpos[0] = vec_len(pkt_ptr->pos); - cellcoordmax[0] = grid::get_cellcoordmax(cellindex, 0); - vel[0] = dot(pkt_ptr->pos, pkt_ptr->dir) / vec_len(pkt_ptr->pos) * CLIGHT_PROP; // radial velocity - } else { - assert_always(false); - } - - // for (int d = 0; d < ndim; d++) - // { - // if (initpos[d] < grid::get_cellcoordmin(cellindex, d) || initpos[d] > cellcoordmax[d]) - // { - // printout("WARNING: packet should have already escaped.\n"); - // *snext = -99; - // return 0; - // } - // } - - // printout("boundary.c: x0 %g, y0 %g, z0 %g\n", initpos[0] initpos[1] initpos[2]); - // printout("boundary.c: vx %g, vy %g, vz %g\n",vel[0],vel[1],vel[2]); - // printout("boundary.c: cellxmin %g, cellymin %g, cellzmin %g\n",grid::get_cellcoordmin(cellindex, - // 0),grid::get_cellcoordmin(cellindex, 1),grid::get_cellcoordmin(cellindex, 2)); printout("boundary.c: cellxmax %g, - // cellymax %g, cellzmax %g\n",cellcoordmax[0],cellcoordmax[1],cellcoordmax[2]); - - enum cell_boundary last_cross = pkt_ptr->last_cross; - enum cell_boundary const negdirections[3] = {NEG_X, NEG_Y, NEG_Z}; // 'X' might actually be radial coordinate - enum cell_boundary const posdirections[3] = {POS_X, POS_Y, POS_Z}; - - // printout("checking inside cell boundary\n"); - for (int d = 0; d < ndim; d++) { - // flip is either zero or one to indicate +ve and -ve boundaries along the selected axis - for (int flip = 0; flip < 2; flip++) { - enum cell_boundary const direction = flip != 0 ? posdirections[d] : negdirections[d]; - enum cell_boundary const invdirection = flip == 0 ? posdirections[d] : negdirections[d]; - const int cellindexstride = - flip != 0 ? -grid::get_coordcellindexincrement(d) : grid::get_coordcellindexincrement(d); - - bool isoutside_thisside = false; - if (flip != 0) { - isoutside_thisside = initpos[d] < (grid::get_cellcoordmin(cellindex, d) / globals::tmin * tstart - - 10.); // 10 cm accuracy tolerance - } else { - isoutside_thisside = initpos[d] > (cellcoordmax[d] / globals::tmin * tstart + 10.); - } - - if (isoutside_thisside && (last_cross != direction)) { - // for (int d2 = 0; d2 < ndim; d2++) - const int d2 = d; - { - printout( - "[warning] packet %d outside coord %d %c%c boundary of cell %d. pkttype %d initpos(tmin) %g, vel %g, " - "cellcoordmin %g, cellcoordmax %g\n", - pkt_ptr->number, d, flip != 0 ? '-' : '+', grid::coordlabel[d], cellindex, pkt_ptr->type, initpos[d2], - vel[d2], grid::get_cellcoordmin(cellindex, d2) / globals::tmin * tstart, - cellcoordmax[d2] / globals::tmin * tstart); - } - printout("globals::tmin %g tstart %g tstart/globals::tmin %g tdecay %g\n", globals::tmin, tstart, - tstart / globals::tmin, pkt_ptr->tdecay); - // printout("[warning] pkt_ptr->number %d\n", pkt_ptr->number); - if (flip != 0) { - printout("[warning] delta %g\n", - (initpos[d] * globals::tmin / tstart) - grid::get_cellcoordmin(cellindex, d)); - } else { - printout("[warning] delta %g\n", cellcoordmax[d] - (initpos[d] * globals::tmin / tstart)); - } - - printout("[warning] dir [%g, %g, %g]\n", pkt_ptr->dir[0], pkt_ptr->dir[1], pkt_ptr->dir[2]); - if ((vel[d] - (initpos[d] / tstart)) > 0) { - if ((grid::get_cellcoordpointnum(cellindex, d) == (grid::ncoordgrid[d] - 1) && cellindexstride > 0) || - (grid::get_cellcoordpointnum(cellindex, d) == 0 && cellindexstride < 0)) { - printout("escaping packet\n"); - *snext = -99; - return 0; - } - *snext = pkt_ptr->where + cellindexstride; - pkt_ptr->last_cross = invdirection; - printout("[warning] swapping packet cellindex from %d to %d and setting last_cross to %d\n", pkt_ptr->where, - *snext, pkt_ptr->last_cross); - return 0; - } - printout("pretending last_cross is %d\n", direction); - last_cross = direction; - } - } - } - - // printout("pkt_ptr->number %d\n", pkt_ptr->number); - // printout("delta1x %g delta2x %g\n", (initpos[0] * globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 0), - // cellcoordmax[0] - (initpos[0] * globals::tmin/tstart)); printout("delta1y %g delta2y %g\n", (initpos[1] * - // globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 1), cellcoordmax[1] - (initpos[1] * globals::tmin/tstart)); - // printout("delta1z %g delta2z %g\n", (initpos[2] * globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 2), - // cellcoordmax[2] - (initpos[2] * globals::tmin/tstart)); printout("dir [%g, %g, %g]\n", - // pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); - - double t_coordmaxboundary[3]; // time to reach the cell's upper boundary on each coordinate - double t_coordminboundary[3]; // likewise, the lower boundaries (smallest x,y,z or radius value in the cell) - if (GRID_TYPE == GRID_SPHERICAL1D) { - last_cross = NONE; // we will handle this separately by setting d_inner and d_outer negative for invalid directions - const double r_inner = grid::get_cellcoordmin(cellindex, 0) * tstart / globals::tmin; - - const double d_inner = (r_inner > 0.) ? get_shellcrossdist(pkt_ptr->pos, pkt_ptr->dir, r_inner, true, tstart) : -1.; - t_coordminboundary[0] = d_inner / CLIGHT_PROP; - - const double r_outer = cellcoordmax[0] * tstart / globals::tmin; - const double d_outer = get_shellcrossdist(pkt_ptr->pos, pkt_ptr->dir, r_outer, false, tstart); - t_coordmaxboundary[0] = d_outer / CLIGHT_PROP; - - // printout("cell %d\n", pkt_ptr->where); - // printout("initradius %g: velrad %g\n", initpos[0], vel[0]); - // printout("d_outer %g d_inner %g \n", d_outer, d_inner); - // printout("t_plus %g t_minus %g \n", t_coordmaxboundary[0], t_coordminboundary[0]); - // printout("cellrmin %g cellrmax %g\n", - // grid::get_cellcoordmin(cellindex, 0) * tstart / globals::tmin, cellcoordmax[0] * tstart / - // globals::tmin); - // printout("tstart %g\n", tstart); - } else { - // const double overshoot = grid::wid_init(cellindex) * 2e-7; - constexpr double overshoot = 0.; - for (int d = 0; d < 3; d++) { - t_coordmaxboundary[d] = ((initpos[d] - (vel[d] * tstart)) / - ((cellcoordmax[d] + overshoot) - (vel[d] * globals::tmin)) * globals::tmin) - - tstart; - - t_coordminboundary[d] = - ((initpos[d] - (vel[d] * tstart)) / - ((grid::get_cellcoordmin(cellindex, d) - overshoot) - (vel[d] * globals::tmin)) * globals::tmin) - - tstart; - } - } - - // printout("comparing distances. last_cross = %d\n", last_cross); - // We now need to identify the shortest +ve time - that's the one we want. - int choice = 0; /// just a control variable to - double time = 1.e99; - // close = 1.e99; - // printout("bondary.c check value of last_cross = %d\n",last_cross); - for (int d = 0; d < ndim; d++) { - if ((t_coordmaxboundary[d] > 0) && (t_coordmaxboundary[d] < time) && (last_cross != negdirections[d])) { - choice = posdirections[d]; - time = t_coordmaxboundary[d]; - // equivalently if (nxyz[d] == (grid::ncoordgrid[d] - 1)) - // if (grid::get_cellcoordmin(cellindex, d) + 1.5 * grid::wid_init > coordmax[d]) - if (grid::get_cellcoordpointnum(cellindex, d) == (grid::ncoordgrid[d] - 1)) { - *snext = -99; - } else { - *snext = pkt_ptr->where + grid::get_coordcellindexincrement(d); - pkt_ptr->last_cross = posdirections[d]; - } - } - - if ((t_coordminboundary[d] > 0) && (t_coordminboundary[d] < time) && (last_cross != posdirections[d])) { - choice = negdirections[d]; - time = t_coordminboundary[d]; - // equivalently if (nxyz[d] == 0) - // if (grid::get_cellcoordmin(cellindex, d) < - coordmax[d] + 0.5 * grid::wid_init) - if (grid::get_cellcoordpointnum(cellindex, d) == 0) { - *snext = -99; - } else { - *snext = pkt_ptr->where - grid::get_coordcellindexincrement(d); - pkt_ptr->last_cross = negdirections[d]; - } - } - } - - if (choice == 0) { - printout("Something wrong in boundary crossing - didn't find anything.\n"); - printout("packet %d cell %d or %d\n", pkt_ptr->number, cellindex, pkt_ptr->where); - printout("choice %d\n", choice); - printout("globals::tmin %g tstart %g\n", globals::tmin, tstart); - printout("last_cross %d, type %d\n", last_cross, pkt_ptr->type); - for (int d2 = 0; d2 < 3; d2++) { - printout("coord %d: initpos %g dir %g\n", d2, pkt_ptr->pos[d2], pkt_ptr->dir[d2]); - } - printout("|initpos| %g |dir| %g |pos.dir| %g\n", vec_len(pkt_ptr->pos), vec_len(pkt_ptr->dir), - dot(pkt_ptr->pos, pkt_ptr->dir)); - for (int d2 = 0; d2 < ndim; d2++) { - printout("coord %d: txyz_plus %g txyz_minus %g \n", d2, t_coordmaxboundary[d2], t_coordminboundary[d2]); - printout("coord %d: cellcoordmin %g cellcoordmax %g\n", d2, - grid::get_cellcoordmin(cellindex, d2) * tstart / globals::tmin, - cellcoordmax[d2] * tstart / globals::tmin); - } - printout("tstart %g\n", tstart); - - // abort(); - } - - // Now we know what happens. The distance to crossing is.... - double const distance = CLIGHT_PROP * time; - // printout("boundary_cross: time %g distance %g\n", time, distance); - // closest = close; - - return distance; -} - -void change_cell(struct packet *pkt_ptr, int snext) -/// Routine to take a packet across a boundary. -{ - // const int cellindex = pkt_ptr->where; - // printout("[debug] cellnumber %d nne %g\n", cellindex, grid::get_nne(grid::get_cell_modelgridindex(cellindex))); - // printout("[debug] snext %d\n", snext); - - if (snext == -99) { - // Then the packet is exiting the grid. We need to record - // where and at what time it leaves the grid. - pkt_ptr->escape_type = pkt_ptr->type; - pkt_ptr->escape_time = pkt_ptr->prop_time; - pkt_ptr->type = TYPE_ESCAPE; - safeincrement(globals::nesc); - } else { - // Just need to update "where". - pkt_ptr->where = snext; - - stats::increment(stats::COUNTER_CELLCROSSINGS); - } -} - -static auto get_cell(const double pos[3], double t) -> int -/// identify the cell index from a position and a time. -{ - assert_always(GRID_TYPE == GRID_UNIFORM); // other grid types not implemented yet - - const double trat = t / globals::tmin; - const int nx = static_cast((pos[0] - (grid::get_cellcoordmin(0, 0) * trat)) / (grid::wid_init(0) * trat)); - const int ny = static_cast((pos[1] - (grid::get_cellcoordmin(0, 1) * trat)) / (grid::wid_init(0) * trat)); - const int nz = static_cast((pos[2] - (grid::get_cellcoordmin(0, 2) * trat)) / (grid::wid_init(0) * trat)); - - const int cellindex = nx + (grid::ncoordgrid[0] * ny) + (grid::ncoordgrid[0] * grid::ncoordgrid[1] * nz); - - // do a check - for (int n = 0; n < grid::get_ngriddimensions(); n++) { - assert_always(pos[n] >= grid::get_cellcoordmin(cellindex, n)); - assert_always(pos[n] <= grid::get_cellcoordmax(cellindex, n)); - } - return cellindex; -} \ No newline at end of file diff --git a/boundary.h b/boundary.h deleted file mode 100644 index caf24d275..000000000 --- a/boundary.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef BOUNDARY_H -#define BOUNDARY_H - -enum cell_boundary { - NEG_X = 101, - POS_X = 102, - NEG_Y = 103, - POS_Y = 104, - NEG_Z = 105, - POS_Z = 106, - NONE = 107, -}; - -double boundary_cross(struct packet *pkt_ptr, int *snext); -void change_cell(struct packet *pkt_ptr, int snext); - -#endif // BOUNDARY_H diff --git a/constants.h b/constants.h index f66b6cf53..fc1f7e2df 100644 --- a/constants.h +++ b/constants.h @@ -1,6 +1,8 @@ #ifndef CONSTANTS_H #define CONSTANTS_H +#include + /// fundamental constants constexpr double CLIGHT = 2.99792458e+10; /// Speed of light [cm/s] constexpr double CLIGHT_PROP = CLIGHT; // Speed of light for ray travel. Physically = CLIGHT but @@ -11,7 +13,7 @@ constexpr double LSUN = 3.826e+33; /// Solar luminosity [erg/s] constexpr double MH = 1.67352e-24; /// Mass of hydrogen atom [g] constexpr double ME = 9.1093897e-28; /// Mass of free electron [g] constexpr double QE = 4.80325E-10; /// elementary charge in cgs units [statcoulomb] -constexpr double PI = 3.1415926535987; +constexpr double PI = M_PI; constexpr double EV = 1.6021772e-12; /// eV to ergs [eV/erg] constexpr double MEV = 1.6021772e-6; /// MeV to ergs [MeV/erg] constexpr double DAY = 86400.0; /// day to seconds [s/day] @@ -38,8 +40,12 @@ constexpr double OSCSTRENGTHCONVERSION = 1.3473837e+21; constexpr double H_ionpot = 13.5979996 * EV; -constexpr int GRID_UNIFORM = 1; // Simple cuboidal cells. -constexpr int GRID_SPHERICAL1D = 2; // radial shells +enum gridtypes { + GRID_SPHERICAL1D = 1, // 1D radial shells (non-uniform dr) + GRID_CYLINDRICAL2D = 2, // 2D cylindrical grid with uniform dz, drcyl + GRID_CARTESIAN3D = 3 // 3D Cartesian cubic grid with uniform dx=dy=dz +}; +constexpr int GRID_UNIFORM = GRID_CARTESIAN3D; // deprecated alias for GRID_CARTESIAN3D // constant for van-Regemorter approximation. constexpr double C_0 = 5.465e-11; diff --git a/decay.cc b/decay.cc index 9ca16eddb..2015ebe5c 100644 --- a/decay.cc +++ b/decay.cc @@ -1,15 +1,19 @@ #include "decay.h" -#include // std::max +#include +#include #include #include #include #include #include +#include +#include #include #include #include #include +#include #include #include "atomic.h" @@ -22,8 +26,8 @@ namespace decay { -constexpr int Z_MAX = 119; -const char *elsymbols[1 + Z_MAX] = { +constexpr int Z_MAX = 118; +constexpr std::string_view elsymbols[Z_MAX + 1] = { "n", "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", @@ -33,30 +37,82 @@ const char *elsymbols[1 + Z_MAX] = { "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Uut", "Fl", "Uup", "Lv", "Uus", "Uuo"}; struct nuclide { - int z; // atomic number - int a; // mass number - double meanlife; // mean lifetime before decay [s] - double endecay_electron; // average energy per beta- decay in kinetic energy of emitted electons [erg] - double endecay_positron; // average energy per beta+ decay in kinetic energy of emitted positrons [erg] - double endecay_gamma; // average energy per decay in gamma rays [erg] - double endecay_alpha; // average energy per alpha decay in kinetic energy of alpha particles [erg] - double endecay_q[DECAYTYPE_COUNT]; // Q-value for decay (reactant minus product energy) of each decay type - double branchprobs[DECAYTYPE_COUNT]; // branch probabilities of each decay type + int z = -1; // atomic number + int a = -1; // mass number + double meanlife = -1; // mean lifetime before decay [s] + double endecay_electron = 0.; // average energy per beta- decay in kinetic energy of emitted electons [erg] + double endecay_positron = 0.; // average energy per beta+ decay in kinetic energy of emitted positrons [erg] + double endecay_gamma = 0.; // average energy per decay in gamma rays [erg] + double endecay_alpha = 0.; // average energy per alpha decay in kinetic energy of alpha particles [erg] + std::array endecay_q = { + 0.}; // Q-value (reactant minus product energy) for each decay type + std::array branchprobs = {0.}; // branch probability of each decay type }; std::vector nuclides; +constexpr auto decay_daughter_z(const int z_parent, const int /*a_parent*/, const int decaytype) -> int +// check if (z_parent, a_parent) is a parent of (z, a) +{ + assert_always(decaytype >= 0); + assert_always(decaytype < decaytypes::DECAYTYPE_COUNT); + + switch (static_cast(decaytype)) { + case decaytypes::DECAYTYPE_ALPHA: { + return z_parent - 2; // lose two protons and two neutrons + } + case decaytypes::DECAYTYPE_BETAPLUS: + case decaytypes::DECAYTYPE_ELECTRONCAPTURE: { + return z_parent - 1; // lose a proton, gain a neutron + } + case decaytypes::DECAYTYPE_BETAMINUS: { + return z_parent + 1; // lose a neutron, gain a proton + } + case decaytypes::DECAYTYPE_NONE: { + return -1; // no daughter + } + case decaytypes::DECAYTYPE_COUNT: { + assert_always(false); + } + } + return -1; // no daughter +} + +constexpr auto decay_daughter_a(const int /*z_parent*/, const int a_parent, const int decaytype) -> int +// check if (z_parent, a_parent) is a parent of (z, a) +{ + switch (static_cast(decaytype)) { + case decaytypes::DECAYTYPE_ALPHA: { + return a_parent - 4; // lose two protons and two neutrons + } + case decaytypes::DECAYTYPE_BETAPLUS: + case decaytypes::DECAYTYPE_ELECTRONCAPTURE: + case decaytypes::DECAYTYPE_BETAMINUS: { + return a_parent; // swap a neutron to proton or vice-versa + } + case decaytypes::DECAYTYPE_NONE: { + return -1; // no daughter + } + case decaytypes::DECAYTYPE_COUNT: { + assert_always(false); + } + } + return -1; // no daughter +} + // a decay path follows the contribution from an initial nuclear abundance // to another (daughter of last nuclide in decaypath) via decays // every different path within the network is considered, e.g. 56Ni -> 56Co -> 56Fe is separate to 56Ni -> 56Co struct decaypath { - int pathlength{}; std::vector z{}; // atomic number std::vector a{}; // mass number std::vector nucindex{}; // index into nuclides list std::vector decaytypes{}; std::vector lambdas{}; double branchproduct{}; // product of all branching factors along the path set by calculate_decaypath_branchproduct() + + [[nodiscard]] auto final_daughter_a() const -> int { return decay_daughter_a(z.back(), a.back(), decaytypes.back()); } + [[nodiscard]] auto final_daughter_z() const -> int { return decay_daughter_z(z.back(), a.back(), decaytypes.back()); } }; std::vector decaypaths; @@ -72,8 +128,8 @@ MPI_Win win_decaypath_energy_per_mass = MPI_WIN_NULL; auto get_num_nuclides() -> int { return nuclides.size(); } auto get_elname(const int z) -> const char * { - assert_always(z <= Z_MAX); - return elsymbols[z]; + assert_testmodeonly(z <= Z_MAX); + return &elsymbols[z].front(); } auto get_nuc_z(int nucindex) -> int { @@ -145,60 +201,11 @@ static void printout_nuclidemeanlife(const int z, const int a) { } } -constexpr auto decay_daughter_z(const int z_parent, const int /*a_parent*/, int decaytype) -> int -// check if (z_parent, a_parent) is a parent of (z, a) -{ - assert_always(decaytype >= 0); - assert_always(decaytype < DECAYTYPE_COUNT); - - switch (static_cast(decaytype)) { - case DECAYTYPE_ALPHA: { - return z_parent - 2; // lose two protons and two neutrons - } - case DECAYTYPE_BETAPLUS: - case DECAYTYPE_ELECTRONCAPTURE: { - return z_parent - 1; // lose a proton, gain a neutron - } - case DECAYTYPE_BETAMINUS: { - return z_parent + 1; // lose a neutron, gain a proton - } - case DECAYTYPE_NONE: { - return -1; // no daughter - } - case DECAYTYPE_COUNT: { - assert_always(false); - } - } - return -1; // no daughter -} - -constexpr auto decay_daughter_a(const int /*z_parent*/, const int a_parent, int decaytype) -> int -// check if (z_parent, a_parent) is a parent of (z, a) -{ - switch (static_cast(decaytype)) { - case DECAYTYPE_ALPHA: { - return a_parent - 4; // lose two protons and two neutrons - } - case DECAYTYPE_BETAPLUS: - case DECAYTYPE_ELECTRONCAPTURE: - case DECAYTYPE_BETAMINUS: { - return a_parent; // swap a neutron to proton or vice-versa - } - case DECAYTYPE_NONE: { - return -1; // no daughter - } - case DECAYTYPE_COUNT: { - assert_always(false); - } - } - return -1; // no daughter -} - static auto get_nuc_decaybranchprob(const int nucindex, const int decaytype) -> double { assert_testmodeonly(nucindex >= 0); assert_testmodeonly(nucindex < get_num_nuclides()); assert_testmodeonly(decaytype >= 0); - assert_testmodeonly(decaytype < DECAYTYPE_COUNT); + assert_testmodeonly(decaytype < decaytypes::DECAYTYPE_COUNT); return nuclides[nucindex].branchprobs[decaytype]; } @@ -211,14 +218,9 @@ static auto nuc_is_parent(const int z_parent, const int a_parent, const int z, c { assert_testmodeonly(nuc_exists(z_parent, a_parent)); // each radioactive nuclide is limited to one daughter nuclide - for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) { - if (decay_daughter_z(z_parent, a_parent, dectypeindex) == z && - decay_daughter_a(z_parent, a_parent, dectypeindex) == a && - get_nuc_decaybranchprob(z_parent, a_parent, dectypeindex) > 0.) { - return true; - } - } - return false; + return std::ranges::any_of(all_decaytypes, [=](const auto decaytype) { + return decay_daughter_z(z_parent, a_parent, decaytype) == z && decay_daughter_a(z_parent, a_parent, decaytype) == a; + }); } auto nucdecayenergygamma(int nucindex) -> double @@ -239,19 +241,19 @@ static auto nucdecayenergyparticle(const int nucindex, const int decaytype) -> d // depending on the relevant decay type (but not including neutrinos) { assert_testmodeonly(decaytype >= 0); - assert_testmodeonly(decaytype < DECAYTYPE_COUNT); + assert_testmodeonly(decaytype < decaytypes::DECAYTYPE_COUNT); switch (decaytype) { - case DECAYTYPE_ALPHA: { + case decaytypes::DECAYTYPE_ALPHA: { return nuclides[nucindex].endecay_alpha; } - case DECAYTYPE_BETAPLUS: { + case decaytypes::DECAYTYPE_BETAPLUS: { return nuclides[nucindex].endecay_positron; } - case DECAYTYPE_ELECTRONCAPTURE: { + case decaytypes::DECAYTYPE_ELECTRONCAPTURE: { return 0.; } - case DECAYTYPE_BETAMINUS: { + case decaytypes::DECAYTYPE_BETAMINUS: { return nuclides[nucindex].endecay_electron; } default: { @@ -264,17 +266,16 @@ static auto nucdecayenergytotal(const int z, const int a) -> double // average energy (erg) per decay in the form of gammas and particles [erg] { const int nucindex = get_nucindex(z, a); - double endecay = 0.; - endecay += nuclides[nucindex].endecay_gamma; - for (int decaytype = 0; decaytype < DECAYTYPE_COUNT; decaytype++) { - endecay += nucdecayenergyparticle(nucindex, decaytype) * get_nuc_decaybranchprob(nucindex, decaytype); - } + const auto endecay_particles = std::accumulate( + all_decaytypes.cbegin(), all_decaytypes.cend(), 0., [nucindex](const double ensum, const auto &decaytype) { + return ensum + nucdecayenergyparticle(nucindex, decaytype) * get_nuc_decaybranchprob(nucindex, decaytype); + }); - return endecay; + return nuclides[nucindex].endecay_gamma + endecay_particles; } static auto nucdecayenergy(int nucindex, int decaytype) -> double -// contributed energy release per decay [erg] for decaytype (e.g. DECAYTYPE_BETAPLUS) +// contributed energy release per decay [erg] for decaytype (e.g. decaytypes::DECAYTYPE_BETAPLUS) // (excludes neutrinos!) { const double endecay = nuclides[nucindex].endecay_gamma + nucdecayenergyparticle(nucindex, decaytype); @@ -286,57 +287,58 @@ static auto nucdecayenergyqval(int nucindex, int decaytype) -> double { return nuclides[nucindex].endecay_q[decaytype]; } -auto nucmass(int z, int a) -> double { - assert_testmodeonly(z > 0); - assert_testmodeonly(a >= z); - - return a * MH; - - // return nuclides[nucindex].amass; -} - -static auto get_num_decaypaths() -> int { return decaypaths.size(); } +static auto get_num_decaypaths() -> int { return static_cast(decaypaths.size()); } -static auto get_decaypathlength(int decaypathindex) -> int { return decaypaths[decaypathindex].pathlength; } +static auto get_decaypathlength(const decaypath &dpath) -> int { return static_cast(dpath.z.size()); } +static auto get_decaypathlength(int decaypathindex) -> int { return get_decaypathlength(decaypaths[decaypathindex]); } -static auto calculate_decaypath_branchproduct(int decaypathindex) -> double +static auto calculate_decaypath_branchproduct(const decaypath &decaypath) -> double // return the product of all branching factors in the decay path { double branchprod = 1.; - for (int i = 0; i < get_decaypathlength(decaypathindex); i++) { - branchprod = branchprod * get_nuc_decaybranchprob(decaypaths[decaypathindex].nucindex[i], - decaypaths[decaypathindex].decaytypes[i]); + for (int i = 0; i < get_decaypathlength(decaypath); i++) { + branchprod = branchprod * get_nuc_decaybranchprob(decaypath.nucindex[i], decaypath.decaytypes[i]); } return branchprod; } -static auto get_decaypath_lastnucdecayenergy(const int decaypathindex) -> double +static auto calculate_decaypath_branchproduct(int decaypathindex) -> double +// return the product of all branching factors in the decay path +{ + return calculate_decaypath_branchproduct(decaypaths[decaypathindex]); +} + +static auto get_decaypath_lastnucdecayenergy(const decaypath &dpath) -> double // a decaypath's energy is the decay energy of the last nuclide and decaytype in the chain { - const int nucindex_end = decaypaths[decaypathindex].nucindex[get_decaypathlength(decaypathindex) - 1]; - const int decaytype_end = decaypaths[decaypathindex].decaytypes[get_decaypathlength(decaypathindex) - 1]; + const int nucindex_end = dpath.nucindex.back(); + const int decaytype_end = dpath.decaytypes.back(); return nucdecayenergy(nucindex_end, decaytype_end); } +static auto get_decaypath_lastnucdecayenergy(const int decaypathindex) -> double { + return get_decaypath_lastnucdecayenergy(decaypaths[decaypathindex]); +} + static void printout_decaytype(const int decaytype) { switch (decaytype) { - case DECAYTYPE_ALPHA: { + case decaytypes::DECAYTYPE_ALPHA: { printout("alpha"); break; } - case DECAYTYPE_BETAPLUS: { + case decaytypes::DECAYTYPE_BETAPLUS: { printout("beta+"); break; } - case DECAYTYPE_ELECTRONCAPTURE: { + case decaytypes::DECAYTYPE_ELECTRONCAPTURE: { printout("ec"); break; } - case DECAYTYPE_BETAMINUS: { + case decaytypes::DECAYTYPE_BETAMINUS: { printout("beta-"); break; } - case DECAYTYPE_NONE: { + case decaytypes::DECAYTYPE_NONE: { printout("none"); break; } @@ -347,27 +349,25 @@ static void printout_decaytype(const int decaytype) { static void printout_decaypath(const int decaypathindex) { assert_always(!decaypaths.empty()); + const auto &decaypath = decaypaths[decaypathindex]; printout(" decaypath %d: ", decaypathindex); - printout_nuclidename(decaypaths[decaypathindex].z[0], decaypaths[decaypathindex].a[0]); - printout_nuclidemeanlife(decaypaths[decaypathindex].z[0], decaypaths[decaypathindex].a[0]); - - for (int i = 1; i < get_decaypathlength(decaypathindex); i++) { - printout(" -> "); - printout_decaytype(decaypaths[decaypathindex].decaytypes[i - 1]); - printout(" -> "); - printout_nuclidename(decaypaths[decaypathindex].z[i], decaypaths[decaypathindex].a[i]); - printout_nuclidemeanlife(decaypaths[decaypathindex].z[i], decaypaths[decaypathindex].a[i]); + + for (int i = 0; i < get_decaypathlength(decaypathindex); i++) { + printout_nuclidename(decaypath.z[i], decaypath.a[i]); + printout_nuclidemeanlife(decaypath.z[i], decaypath.a[i]); + + if (decaypath.decaytypes[i] != DECAYTYPE_NONE) { + printout(" -> "); + printout_decaytype(decaypath.decaytypes[i]); + printout(" -> "); + } + } + + // if the last nuclide is unstable, print its daughter nucleus + if (decaypath.decaytypes.back() != DECAYTYPE_NONE) { + printout_nuclidename(decaypath.final_daughter_z(), decaypath.final_daughter_a()); + printout_nuclidemeanlife(decaypath.final_daughter_z(), decaypath.final_daughter_a()); } - const int last_z = decaypaths[decaypathindex].z[get_decaypathlength(decaypathindex) - 1]; - const int last_a = decaypaths[decaypathindex].a[get_decaypathlength(decaypathindex) - 1]; - const int last_decaytype = decaypaths[decaypathindex].decaytypes[get_decaypathlength(decaypathindex) - 1]; - const int end_z = decay_daughter_z(last_z, last_a, last_decaytype); - const int end_a = decay_daughter_a(last_z, last_a, last_decaytype); - printout(" -> "); - printout_decaytype(last_decaytype); - printout(" -> "); - printout_nuclidename(end_z, end_a); - printout_nuclidemeanlife(end_z, end_a); printout("\n"); } @@ -377,15 +377,12 @@ static void extend_lastdecaypath() // to get decaypaths from all descendants { const int startdecaypathindex = decaypaths.size() - 1; - const int last_z = decaypaths[startdecaypathindex].z[get_decaypathlength(startdecaypathindex) - 1]; - const int last_a = decaypaths[startdecaypathindex].a[get_decaypathlength(startdecaypathindex) - 1]; - const int dectypeindex = decaypaths[startdecaypathindex].decaytypes[get_decaypathlength(startdecaypathindex) - 1]; - const int daughter_z = decay_daughter_z(last_z, last_a, dectypeindex); - const int daughter_a = decay_daughter_a(last_z, last_a, dectypeindex); + const int daughter_z = decaypaths[startdecaypathindex].final_daughter_z(); + const int daughter_a = decaypaths[startdecaypathindex].final_daughter_a(); if (nuc_exists(daughter_z, daughter_a)) { const int daughter_nucindex = get_nucindex(daughter_z, daughter_a); - for (int dectypeindex2 = 0; dectypeindex2 < DECAYTYPE_COUNT; dectypeindex2++) { + for (enum decaytypes dectypeindex2 : all_decaytypes) { if (get_nuc_decaybranchprob(daughter_nucindex, dectypeindex2) == 0.) { continue; } @@ -398,11 +395,9 @@ static void extend_lastdecaypath() } } - const int newpathlength = get_decaypathlength(startdecaypathindex) + 1; decaypaths.push_back(decaypaths[startdecaypathindex]); const int lastindex = decaypaths.size() - 1; - decaypaths[lastindex].pathlength = newpathlength; decaypaths[lastindex].z.push_back(daughter_z); decaypaths[lastindex].a.push_back(daughter_a); decaypaths[lastindex].nucindex.push_back(daughter_nucindex); @@ -413,51 +408,60 @@ static void extend_lastdecaypath() } } -constexpr auto operator<(const struct decaypath &d1, const struct decaypath &d2) -> bool +static auto operator<(const struct decaypath &d1, const struct decaypath &d2) -> bool // true if d1 < d2 -// order the chains in the same way as when the search moved up from the descendant -// instead of down from the ancestor, for ease of test comparison -// chains are sorted by mass number of first, second, third, etc position in chain +// chains are sorted by mass number, then atomic number, then length { - const int smallestpathlength = std::min(d1.pathlength, d2.pathlength); - bool matchingoverlap = true; + const int d1_length = get_decaypathlength(d1); + const int d2_length = get_decaypathlength(d2); + const int smallestpathlength = std::min(d1_length, d2_length); for (int i = 0; i < smallestpathlength; i++) { - const int d1pos = d1.pathlength - 1 - i; - // assert_always(d1pos >= 0); - // assert_always(d1pos < d1.pathlength); - const int d2pos = d2.pathlength - 1 - i; - // assert_always(d2pos >= 0); - // assert_always(d2pos < d2.pathlength); - // if (get_nucindex(d1.z[d1pos], d1.a[d1pos]) < get_nucindex(d2.z[d2pos], d2.a[d2pos])) - if (d1.a[d1pos] < d2.a[d2pos]) { + if (d1.a[i] < d2.a[i]) { return true; } - if (d1.a[d1pos] == d2.a[d2pos] && d1.z[d1pos] < d2.z[d2pos]) { + if (d1.a[i] > d2.a[i]) { + return false; + } + if (d1.z[i] < d2.z[i]) { return true; } - if (d1.a[d1pos] != d2.a[d2pos] || d1.z[d1pos] != d2.z[d2pos]) { - matchingoverlap = false; + if (d1.z[i] > d2.z[i]) { + return false; } } // one is an extension of the other - return matchingoverlap && d1.pathlength < d2.pathlength; + return d1_length < d2_length; } -static void find_decaypaths() { +static void find_decaypaths(const std::vector &custom_zlist, const std::vector &custom_alist, + std::vector &standard_nuclides) { + decaypaths.clear(); for (int startnucindex = 0; startnucindex < get_num_nuclides(); startnucindex++) { const int z = get_nuc_z(startnucindex); const int a = get_nuc_a(startnucindex); - for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) { - if (get_nuc_decaybranchprob(startnucindex, dectypeindex) == 0. || get_meanlife(startnucindex) <= 0.) { + for (const auto decaytype : all_decaytypes) { + if (get_nuc_decaybranchprob(startnucindex, decaytype) == 0. || get_meanlife(startnucindex) <= 0.) { + continue; + } + bool is_custom_nuclide = false; + for (size_t i = 0; i < custom_zlist.size(); i++) { + if ((z == custom_zlist[i]) && (a == custom_alist[i])) { + is_custom_nuclide = true; + break; + } + } + // skip path if it doesn't start from a nuclide in the custom or standard input lists + if (!is_custom_nuclide && !std::ranges::any_of(standard_nuclides, [z, a](const auto &stdnuc) { + return (z == stdnuc.z) && (a == stdnuc.a); + })) { continue; } - decaypaths.push_back({.pathlength = 1, - .z = std::vector(1, z), + decaypaths.push_back({.z = std::vector(1, z), .a = std::vector(1, a), .nucindex = std::vector(1, startnucindex), - .decaytypes = std::vector(1, dectypeindex)}); + .decaytypes = std::vector(1, decaytype)}); extend_lastdecaypath(); // take this single step chain and find all descendants } @@ -465,23 +469,71 @@ static void find_decaypaths() { std::sort(decaypaths.begin(), decaypaths.end()); - for (int decaypathindex = 0; decaypathindex < get_num_decaypaths(); decaypathindex++) { - int const decaypathlength = get_decaypathlength(decaypathindex); - - for (int i = 0; i < decaypathlength; i++) { - const double meanlife = get_meanlife(decaypaths[decaypathindex].nucindex[i]); - - // last nuclide might be stable (meanlife <= 0.) - assert_always(meanlife > 0. || (i == decaypathlength - 1)); // only the last nuclide can be stable - const double lambda = (meanlife > 0.) ? 1. / meanlife : 0.; - - decaypaths[decaypathindex].lambdas.push_back(lambda); - } - - // the nuclide past the end of the path is a used as a sink, so treat it as stable (even if it's not) - decaypaths[decaypathindex].lambdas.push_back(0.); + for (auto &decaypath : decaypaths) { + // all nuclei in the path (except for the last one, which is allowed to be stable) must have a mean life >0 + assert_always(std::all_of(decaypath.nucindex.cbegin(), decaypath.nucindex.cend() - 1, + [](const auto nucindex) { return get_meanlife(nucindex) > 0.; })); + + // convert mean lifetimes to decay constants + decaypath.lambdas.resize(decaypath.nucindex.size()); + std::transform(decaypath.nucindex.cbegin(), decaypath.nucindex.cend(), decaypath.lambdas.begin(), + [](const auto nucindex) { + const double meanlife = get_meanlife(nucindex); + // last nuclide might be stable (meanlife <= 0.) + const double lambda = (meanlife > 0.) ? 1. / meanlife : 0.; + return lambda; + }); + + // the nuclide one past the end of the path is a used as a sink, so treat it as stable (even if it's not) + decaypath.lambdas.push_back(0.); + + decaypath.branchproduct = calculate_decaypath_branchproduct(decaypath); + } + decaypaths.shrink_to_fit(); +} - decaypaths[decaypathindex].branchproduct = calculate_decaypath_branchproduct(decaypathindex); +static void filter_unused_nuclides(const std::vector &custom_zlist, const std::vector &custom_alist, + std::vector &standard_nuclides) { + // remove nuclides that are not a standard or custom input-specified nuclide, or connected to these by decays + nuclides.erase( + std::remove_if(nuclides.begin(), nuclides.end(), + [&](const auto &nuc) { + // keep nucleus if it is in the standard list + if (std::ranges::any_of(standard_nuclides, [&](const auto &stdnuc) { + return (stdnuc.z == nuc.z) && (stdnuc.a == nuc.a); + })) { + return false; + } + // keep nucleus if it is in the custom list + for (size_t i = 0; i < custom_zlist.size(); i++) { + if ((nuc.z == custom_zlist[i]) && (nuc.a == custom_alist[i])) { + return false; + } + } + + // keep if it is connected by decays to one of the standard or custom input-specified nuclides + for (const auto &decaypath : decaypaths) { + if (decaypath.final_daughter_z() == nuc.z && decaypath.final_daughter_a() == nuc.a) { + return false; + } + + for (size_t i = 0; i < decaypath.z.size(); i++) { + if (decaypath.z[i] == nuc.z && decaypath.a[i] == nuc.a) { + // decay path starts with input nuc and nuc is in the decay path, so keep it + return false; + }; + } + } + printout("removing unused nuclide (Z=%d)%s-%d\n", nuc.z, get_elname(nuc.z), nuc.a); + return true; + }), + nuclides.end()); + nuclides.shrink_to_fit(); + + // update the nuclide indicies in the decay paths after we possibly removed some nuclides + for (auto &decaypath : decaypaths) { + std::transform(decaypath.z.cbegin(), decaypath.z.cend(), decaypath.a.cbegin(), decaypath.nucindex.begin(), + [](const auto z, const auto a) { return get_nucindex(z, a); }); } } @@ -491,7 +543,7 @@ auto get_nucstring_z(const std::string &strnuc) -> int std::string elcode = strnuc; elcode.erase(std::remove_if(elcode.begin(), elcode.end(), &isdigit), elcode.end()); - for (int z = 1; z <= Z_MAX; z++) { + for (int z = 0; z <= Z_MAX; z++) { if (strcmp(elcode.c_str(), get_elname(z)) == 0) // first to letters match el symbol { return z; @@ -521,82 +573,68 @@ auto get_nucstring_a(const std::string &strnuc) -> int return a; } -void init_nuclides(std::vector custom_zlist, std::vector custom_alist) { +void init_nuclides(const std::vector &custom_zlist, const std::vector &custom_alist) { + // add all nuclides and decays, and later trim any irrelevant ones (not connected to input-specified nuclei) later + assert_always(custom_zlist.size() == custom_alist.size()); - struct nuclide default_nuclide {}; - default_nuclide.z = -1; - default_nuclide.a = -1; - default_nuclide.meanlife = -1; - default_nuclide.endecay_electron = 0.; - default_nuclide.endecay_positron = 0.; - default_nuclide.endecay_gamma = 0.; - default_nuclide.endecay_alpha = 0.; - for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) { - default_nuclide.branchprobs[dectypeindex] = 0.; - default_nuclide.endecay_q[dectypeindex] = 0.; - } + // Ni57 + nuclides.push_back({.z = 28, .a = 57, .meanlife = 51.36 * 60}); + nuclides.back().endecay_positron = 0.354 * MEV; + nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 0.436; + nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1. - 0.436; - nuclides.push_back(default_nuclide); - nuclides.back().z = 28; // Ni57 - nuclides.back().a = 57; - nuclides.back().meanlife = 51.36 * 60; - nuclides.back().endecay_positron = 0.354 * MEV * 0.436; - nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 1.; - // nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 0.436; - // nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1. - 0.436; - - nuclides.push_back(default_nuclide); - nuclides.back().z = 28; // Ni56 - nuclides.back().a = 56; - nuclides.back().meanlife = 8.80 * DAY; + // Ni56 + nuclides.push_back({.z = 28, .a = 56, .meanlife = 8.80 * DAY}); nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1.; - nuclides.push_back(default_nuclide); - nuclides.back().z = 27; // Co56 - nuclides.back().a = 56; - nuclides.back().meanlife = 113.7 * DAY; - // old method: move BETAPLUS branching factor into average positron energy - nuclides.back().endecay_positron = 0.63 * MEV * 0.19; - nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 1.; - // should produce the same results if everything is done correctly - // nuclides.back().endecay_positron = 0.63 * MEV; - // nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 0.19; - // nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 0.81; - - nuclides.push_back(default_nuclide); - nuclides.back().z = 24; // Cr48 - nuclides.back().a = 48; - nuclides.back().meanlife = 1.29602 * DAY; + // Co56 + nuclides.push_back({.z = 27, .a = 56, .meanlife = 113.7 * DAY}); + nuclides.back().endecay_positron = 0.63 * MEV; + nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 0.19; + nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1 - 0.19; + + // Cr48 + nuclides.push_back({.z = 24, .a = 48, .meanlife = 1.29602 * DAY}); nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1.; - nuclides.push_back(default_nuclide); - nuclides.back().z = 23; // V48 - nuclides.back().a = 48; - nuclides.back().meanlife = 23.0442 * DAY; + // V48 + nuclides.push_back({.z = 23, .a = 48, .meanlife = 23.0442 * DAY}); nuclides.back().endecay_positron = 0.290 * MEV * 0.499; nuclides.back().branchprobs[DECAYTYPE_BETAPLUS] = 1.; - nuclides.push_back(default_nuclide); - nuclides.back().z = 27; // Co57 - nuclides.back().a = 57; - nuclides.back().meanlife = 392.03 * DAY; + // Co57 + nuclides.push_back({.z = 27, .a = 57, .meanlife = 392.03 * DAY}); nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1.; - nuclides.push_back(default_nuclide); - nuclides.back().z = 26; // Fe52 - nuclides.back().a = 52; - nuclides.back().meanlife = 0.497429 * DAY; + // Fe52 + nuclides.push_back({.z = 26, .a = 52, .meanlife = 0.497429 * DAY}); nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1.; - nuclides.push_back(default_nuclide); - nuclides.back().z = 25; // Mn52 - nuclides.back().a = 52; - nuclides.back().meanlife = 0.0211395 * DAY; + // Mn52 + nuclides.push_back({.z = 25, .a = 52, .meanlife = 0.0211395 * DAY}); nuclides.back().branchprobs[DECAYTYPE_ELECTRONCAPTURE] = 1.; - if (!custom_alist.empty()) { - std::ifstream fbetaminus("betaminusdecays.txt"); + auto standard_nuclides = nuclides; + + // any nuclides in the custom list that are not in the standard list need beta and alpha decay data + + bool use_custom_nuclides = false; + for (size_t i = 0; i < custom_zlist.size(); i++) { + if (custom_zlist[i] < 0 || custom_alist[i] < 0) { + continue; + } + const bool in_std_list = std::ranges::any_of(standard_nuclides, [=](const auto &stdnuc) { + return (custom_zlist[i] == stdnuc.z) && (custom_alist[i] == stdnuc.a); + }); + if (!in_std_list) { + use_custom_nuclides = true; + break; + } + } + + if (use_custom_nuclides) { + auto fbetaminus = fstream_required("betaminusdecays.txt", std::ios::in); assert_always(fbetaminus.is_open()); std::string line; while (get_noncommentline(fbetaminus, line)) { @@ -611,31 +649,18 @@ void init_nuclides(std::vector custom_zlist, std::vector custom_alist) double tau_sec = 0.; std::stringstream(line) >> a >> z >> q_mev >> e_gamma_mev >> e_elec_mev >> e_neutrino >> tau_sec; - bool keeprow = false; // keep if the mass number matches one of the input nuclides - for (int const i : custom_alist) { - if (i == a) { - keeprow = true; - break; - } - } - if (keeprow) { - assert_always(!nuc_exists(z, a)); - nuclides.push_back(default_nuclide); - nuclides.back().z = z; - nuclides.back().a = a; - nuclides.back().meanlife = tau_sec; - nuclides.back().branchprobs[DECAYTYPE_BETAMINUS] = 1.; - nuclides.back().endecay_q[DECAYTYPE_BETAMINUS] = q_mev * MEV; - nuclides.back().endecay_electron = e_elec_mev * MEV; - nuclides.back().endecay_gamma = e_gamma_mev * MEV; - // printout("betaminus file: Adding (Z=%d)%s-%d endecay_electron %g endecay_gamma %g tau_s %g\n", - // z, get_elname(z), a, e_elec_mev, e_gamma_mev, tau_sec); - assert_always(e_elec_mev >= 0.); - } + assert_always(!nuc_exists(z, a)); + nuclides.push_back({.z = z, .a = a, .meanlife = tau_sec}); + nuclides.back().branchprobs[DECAYTYPE_BETAMINUS] = 1.; + nuclides.back().endecay_q[DECAYTYPE_BETAMINUS] = q_mev * MEV; + nuclides.back().endecay_electron = e_elec_mev * MEV; + nuclides.back().endecay_gamma = e_gamma_mev * MEV; + // printout("betaminus file: Adding (Z=%d)%s-%d endecay_electron %g endecay_gamma %g tau_s %g\n", + // z, get_elname(z), a, e_elec_mev, e_gamma_mev, tau_sec); + assert_always(e_elec_mev >= 0.); } - fbetaminus.close(); - std::ifstream falpha("alphadecays.txt"); + auto falpha = fstream_required("alphadecays.txt", std::ios::in); assert_always(falpha.is_open()); while (get_noncommentline(falpha, line)) { // columns: # A, Z, branch_alpha, branch_beta, halflife[s], Q_total_alphadec[MeV], Q_total_betadec[MeV], @@ -653,22 +678,14 @@ void init_nuclides(std::vector custom_zlist, std::vector custom_alist) std::stringstream(line) >> a >> z >> branch_alpha >> branch_beta >> halflife >> Q_total_alphadec >> Q_total_betadec >> e_alpha_mev >> e_gamma_mev >> e_beta_mev; - bool const keeprow = ((branch_alpha > 0. || branch_beta > 0.) && halflife > 0.); + const bool keeprow = ((branch_alpha > 0. || branch_beta > 0.) && halflife > 0.); if (keeprow) { const double tau_sec = halflife / log(2); int alphanucindex = -1; if (nuc_exists(z, a)) { alphanucindex = get_nucindex(z, a); - // printout("compare z %d a %d e_gamma_mev1 %g e_gamma_mev2 %g\n", z, a, nucdecayenergygamma(z, a) / MEV, - // e_gamma_mev); printout("compare z %d a %d tau1 %g tau2 %g\n", z, a, get_meanlife(z, a), tau_sec); - // printout("compare z %d a %d e_beta_mev1 %g e_beta_mev2 %g\n", z, a, nuclides[get_nucindex(z, - // a)].endecay_positron / MEV, e_beta_mev); } else { - nuclides.push_back(default_nuclide); - nuclides.back().z = z; - nuclides.back().a = a; - nuclides.back().meanlife = tau_sec; - nuclides.back().endecay_gamma = e_gamma_mev * MEV; + nuclides.push_back({.z = z, .a = a, .meanlife = tau_sec, .endecay_gamma = e_gamma_mev * MEV}); alphanucindex = nuclides.size() - 1; } nuclides[alphanucindex].endecay_alpha = e_alpha_mev * MEV; @@ -681,45 +698,33 @@ void init_nuclides(std::vector custom_zlist, std::vector custom_alist) // z, get_elname(z), a, e_alpha_mev, e_gamma_mev, tau_sec); } } - falpha.close(); } + // add any extra nuclides that were specified but not in the decay data files for (int i = 0; i < static_cast(custom_alist.size()); i++) { const int z = custom_zlist[i]; const int a = custom_alist[i]; if (!nuc_exists(z, a)) { // printout("Adding Z %d A %d with no decay data (assuming stable)\n", z, a); - nuclides.push_back(default_nuclide); - nuclides.back().z = z; - nuclides.back().a = a; - nuclides.back().meanlife = -1; + nuclides.push_back({.z = z, .a = a, .meanlife = -1}); } } - // TESTING: REMOVE - // for (int i = 0; i < get_num_nuclides(); i++) - // { - // for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) - // { - // nuclides[i].branchprobs[dectypeindex] *= 1e-4; - // nuclides[i].endecay_gamma = 0.; - // } - // } - - printout("init_nuclides: num_nuclides %d\n", get_num_nuclides()); + printout("Number of nuclides before filtering: %d\n", get_num_nuclides()); + find_decaypaths(custom_zlist, custom_alist, standard_nuclides); + filter_unused_nuclides(custom_zlist, custom_alist, standard_nuclides); - /// Read in data for gamma ray lines and make a list of them in energy order. - gammapkt::init_gamma_linelist(); + printout("Number of nuclides: %d\n", get_num_nuclides()); - find_decaypaths(); + const int maxdecaypathlength = std::accumulate( + decaypaths.cbegin(), decaypaths.cend(), 0, + [](const int maxlen, const auto decaypath) { return std::max(maxlen, get_decaypathlength(decaypath)); }); - int maxdecaypathlength = 0; - for (int decaypathindex = 0; decaypathindex < get_num_decaypaths(); decaypathindex++) { - // printout_decaypath(decaypathindex); - maxdecaypathlength = std::max(maxdecaypathlength, get_decaypathlength(decaypathindex)); - } printout("Number of decay paths: %d (max length %d)\n", get_num_decaypaths(), maxdecaypathlength); + /// Read in data for gamma ray lines and make a list of them in energy order. + gammapkt::init_gamma_linelist(); + // TODO: generalise this to all included nuclides printout("decayenergy(NI56), decayenergy(CO56), decayenergy_gamma(CO56): %g, %g, %g\n", nucdecayenergytotal(28, 56) / MEV, nucdecayenergytotal(27, 56) / MEV, nucdecayenergygamma(27, 56) / MEV); @@ -747,16 +752,17 @@ static auto sample_decaytime(const int decaypathindex, const double tdecaymin, c return tdecay; } -static auto calculate_decaychain(const double firstinitabund, std::vector &lambdas, const int num_nuclides, - const double timediff, bool useexpansionfactor) -> double { +static constexpr auto calculate_decaychain(const double firstinitabund, const std::vector &lambdas, + const int num_nuclides, const double timediff, const bool useexpansionfactor) + -> double { // calculate final number abundance from multiple decays, e.g., Ni56 -> Co56 -> Fe56 (nuc[0] -> nuc[1] -> nuc[2]) // the top nuclide initial abundance is set and the chain-end abundance is returned (all intermediates nuclides // are assumed to start with zero abundance) // note: first and last can be nuclide can be the same if num_nuclides==1, reducing to simple decay formula // // timediff: time elapsed since firstinitabund was true [seconds] - // numnuclides: number of items in meanlifetimes to use - // meanlifetimes: array of mean lifetimes for nuc[0]..nuc[num_nuclides-1] [seconds] + // numnuclides: number of items in lambdas to use + // lambdas: array of 1/(mean lifetime) for nuc[0]..nuc[num_nuclides-1] [seconds^-1] // useexpansionfactor: if true, return a modified 'abundance' at the end of the chain, with a weighting factor // accounting for photon energy loss from expansion since the decays occured // (This is needed to get the initial temperature) @@ -809,13 +815,12 @@ static auto get_nuc_massfrac(const int modelgridindex, const int z, const int a, // decay chains include all paths from radionuclides to other radionuclides (including trivial size-one chains) double nuctotal = 0.; // abundance or decay rate, depending on mode parameter - for (int decaypathindex = 0; decaypathindex < get_num_decaypaths(); decaypathindex++) { - const int z_end = decaypaths[decaypathindex].z[get_decaypathlength(decaypathindex) - 1]; - const int a_end = decaypaths[decaypathindex].a[get_decaypathlength(decaypathindex) - 1]; + for (const auto &decaypath : decaypaths) { + const int z_end = decaypath.z.back(); + const int a_end = decaypath.a.back(); // match 4He abundance to alpha decay of any nucleus (no continue), otherwise check daughter nuclide matches - if (z != 2 || a != 4 || - decaypaths[decaypathindex].decaytypes[get_decaypathlength(decaypathindex) - 1] != DECAYTYPE_ALPHA) { + if (z != 2 || a != 4 || decaypath.decaytypes.back() != decaytypes::DECAYTYPE_ALPHA) { if (nuc_exists_z_a && (z_end != z || a_end != a)) // requested nuclide is in network, so match last nuc in chain { continue; @@ -827,9 +832,9 @@ static auto get_nuc_massfrac(const int modelgridindex, const int z, const int a, } } - const int z_top = decaypaths[decaypathindex].z[0]; - const int a_top = decaypaths[decaypathindex].a[0]; - const int nucindex_top = decaypaths[decaypathindex].nucindex[0]; + const int z_top = decaypath.z[0]; + const int a_top = decaypath.a[0]; + const int nucindex_top = decaypath.nucindex[0]; const double top_initabund = grid::get_modelinitradioabund(modelgridindex, nucindex_top) / nucmass(z_top, a_top); assert_always(top_initabund >= 0.); @@ -837,20 +842,19 @@ static auto get_nuc_massfrac(const int modelgridindex, const int z, const int a, continue; } - int const decaypathlength = get_decaypathlength(decaypathindex); + const int decaypathlength = get_decaypathlength(decaypath); int fulldecaypathlength = decaypathlength; // if the nuclide is out of network, it's one past the end of the chain - if (!nuc_exists_z_a || - (z == 2 && a == 4 && - decaypaths[decaypathindex].decaytypes[get_decaypathlength(decaypathindex) - 1] == DECAYTYPE_ALPHA)) { + // or if we're counting alpha particles and the last decaytype is alpha, then the alpha sink is one past the end + if (!nuc_exists_z_a || (z == 2 && a == 4 && decaypath.decaytypes.back() == decaytypes::DECAYTYPE_ALPHA)) { fulldecaypathlength = decaypathlength + 1; } - const double massfraccontrib = (decaypaths[decaypathindex].branchproduct * - calculate_decaychain(top_initabund, decaypaths[decaypathindex].lambdas, - fulldecaypathlength, t_afterinit, false) * - nucmass(z, a)); + const double massfraccontrib = + (decaypath.branchproduct * + calculate_decaychain(top_initabund, decaypath.lambdas, fulldecaypathlength, t_afterinit, false) * + nucmass(z, a)); // assert_always(massfraccontrib >= 0.); nuctotal += massfraccontrib; } @@ -957,32 +961,20 @@ auto get_endecay_per_ejectamass_t0_to_time_withexpansion(const int modelgridinde // the photon energy loss due to expansion between time of decays and tstart (equation 18 of Lucy 2005) { double tot_endecay = 0.; - for (int decaypathindex = 0; decaypathindex < get_num_decaypaths(); decaypathindex++) { - if (get_endecay_to_tinf_per_ejectamass_at_time(modelgridindex, decaypathindex, grid::get_t_model()) <= 0.) { - // skip unused chains - continue; - } - // printout_decaypath(decaypathindex); - // get_endecay_per_ejectamass_t0_to_time_withexpansion_chain_numerical(modelgridindex, decaypathindex, tstart); - - const int decaypathlength = get_decaypathlength(decaypathindex); + for (const auto &decaypath : decaypaths) { + const int decaypathlength = get_decaypathlength(decaypath); - // const double numerator = calculate_decaychain(1., meanlifetimes, decaypathlength + 1, tdiff, true); - // const double factor = numerator / calculate_decaychain(1., meanlifetimes, decaypathlength + 1, tdiff, false); - // printout(" Analytical expansion factor: %g\n", factor); - - const int z_top = decaypaths[decaypathindex].z[0]; - const int a_top = decaypaths[decaypathindex].a[0]; - const int nucindex_top = decaypaths[decaypathindex].nucindex[0]; + const int z_top = decaypath.z[0]; + const int a_top = decaypath.a[0]; + const int nucindex_top = decaypath.nucindex[0]; const double top_initabund = grid::get_modelinitradioabund(modelgridindex, nucindex_top) / nucmass(z_top, a_top); - const double chain_endecay = (decaypaths[decaypathindex].branchproduct * - calculate_decaychain(top_initabund, decaypaths[decaypathindex].lambdas, - decaypathlength + 1, tstart - grid::get_t_model(), true) * - get_decaypath_lastnucdecayenergy(decaypathindex)); + const double chain_endecay = (decaypath.branchproduct * + calculate_decaychain(top_initabund, decaypath.lambdas, decaypathlength + 1, + tstart - grid::get_t_model(), true) * + get_decaypath_lastnucdecayenergy(decaypath)); - // printout(" Analytical chain_endecay: %g\n", chain_endecay); tot_endecay += chain_endecay; } @@ -1044,7 +1036,7 @@ static auto get_decaypath_power_per_ejectamass(const int decaypathindex, const i const double t_afterinit = time - grid::get_t_model(); - int const decaypathlength = get_decaypathlength(decaypathindex); + const int decaypathlength = get_decaypathlength(decaypathindex); // contribution to the end nuclide abundance from the top of chain (could be a length-one chain Z,A_top = Z,A_end // so contribution would be from init abundance only) @@ -1085,7 +1077,7 @@ void setup_decaypath_energy_per_mass() { if (globals::rank_in_node == 0) { my_rank_cells += nonempty_npts_model - (my_rank_cells * globals::node_nprocs); } - MPI_Aint size = my_rank_cells * get_num_decaypaths() * sizeof(double); + auto size = static_cast(my_rank_cells * get_num_decaypaths() * sizeof(double)); int disp_unit = sizeof(double); assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, @@ -1102,7 +1094,7 @@ void setup_decaypath_energy_per_mass() { MPI_Barrier(MPI_COMM_WORLD); #endif - printout("Calculating for decaypath_energy_per_mass for all cells..."); + printout("Calculating decaypath_energy_per_mass for all cells..."); const int num_decaypaths = get_num_decaypaths(); for (int nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { if (nonemptymgi % globals::node_nprocs == globals::rank_in_node) { @@ -1142,9 +1134,6 @@ auto get_particle_injection_rate(const int modelgridindex, const double t, const double dep_sum = 0.; for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { const int z = get_nuc_z(nucindex); - if (z < 1) { - continue; - } const int a = get_nuc_a(nucindex); const double meanlife = get_meanlife(nucindex); if (meanlife < 0.) { @@ -1170,12 +1159,9 @@ auto get_qdot_modelcell(const int modelgridindex, const double t, const int deca double qdot = 0.; for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { const int z = get_nuc_z(nucindex); - if (z < 1) { - continue; - } const int a = get_nuc_a(nucindex); const double meanlife = get_meanlife(nucindex); - if (meanlife <= 0) { + if (meanlife < 0.) { continue; } const double q_decay = nucdecayenergyqval(nucindex, decaytype) * get_nuc_decaybranchprob(nucindex, decaytype); @@ -1203,12 +1189,10 @@ auto get_global_etot_t0_tinf() -> double { } void update_abundances(const int modelgridindex, const int timestep, const double t_current) -/// Updates the mass fractions of elements associated with the decay sequence +/// Updates the mass fractions of elements using the current abundances of nuclides /// Parameters: - modelgridindex: the grid cell for which to update the abundances /// - t_current: current time (here mid of current timestep) { - assert_always(!globals::homogeneous_abundances); // no longer supported - printout("update_abundances for cell %d timestep %d\n", modelgridindex, timestep); for (int element = get_nelements() - 1; element >= 0; element--) { @@ -1231,11 +1215,11 @@ void update_abundances(const int modelgridindex, const int timestep, const doubl } } else { // check if the nucleus decays off the network but into the selected element - for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) { - const int daughter_z = decay_daughter_z(nuc_z, a, dectypeindex); - const int daughter_a = decay_daughter_a(nuc_z, a, dectypeindex); + for (const auto decaytype : all_decaytypes) { + const int daughter_z = decay_daughter_z(nuc_z, a, decaytype); + const int daughter_a = decay_daughter_a(nuc_z, a, decaytype); if (daughter_z == atomic_number && !nuc_exists(daughter_z, daughter_a) && - get_nuc_decaybranchprob(nuc_z, a, dectypeindex) > 0.) { + get_nuc_decaybranchprob(nuc_z, a, decaytype) > 0.) { if (!a_isotopes.contains(daughter_a)) { a_isotopes.insert(daughter_a); // nuclide decays into correct atomic number but outside of the radionuclide list @@ -1265,7 +1249,13 @@ void update_abundances(const int modelgridindex, const int timestep, const doubl grid::set_element_meanweight(modelgridindex, element, isomassfracsum / isomassfrac_on_nucmass_sum); } - // consider calling calculate_electron_densities() here + // total number of electrons in grid cell which are possible targets for compton scattering of gamma rays + double nnetot = 0.; + for (int element = 0; element < get_nelements(); element++) { + const double nnelement = grid::get_elem_numberdens(modelgridindex, element); + nnetot += nnelement * get_atomicnumber(element); + } + grid::set_nnetot(modelgridindex, nnetot); // double initnucfracsum = 0.; // double nucfracsum = 0.; @@ -1280,7 +1270,7 @@ void update_abundances(const int modelgridindex, const int timestep, const doubl // // printout(" init: %g now: %g\n", grid::get_modelinitradioabund(modelgridindex, z, a), // get_nuc_massfrac(modelgridindex, z, a, t_current)); // - // for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) + // for (int dectypeindex = 0; dectypeindex < decaytypes::DECAYTYPE_COUNT; dectypeindex++) // { // if (!nuc_exists(decay_daughter_z(z, a, dectypeindex), decay_daughter_a(z, a, dectypeindex)) && // get_nuc_decaybranchprob(z, a, dectypeindex) > 0.) @@ -1323,12 +1313,12 @@ void fprint_nuc_abundances(FILE *estimators_file, const int modelgridindex, cons } } } else { // not the element that we want, but check if a decay produces it - for (int dectypeindex = 0; dectypeindex < DECAYTYPE_COUNT; dectypeindex++) { - const int daughter_z = decay_daughter_z(nuc_z, nuc_a, dectypeindex); - const int daughter_a = decay_daughter_a(nuc_z, nuc_a, dectypeindex); + for (const auto decaytype : all_decaytypes) { + const int daughter_z = decay_daughter_z(nuc_z, nuc_a, decaytype); + const int daughter_a = decay_daughter_a(nuc_z, nuc_a, decaytype); // if the nucleus exists, it will be picked up by the upper condition if (daughter_z == atomic_number && !nuc_exists(daughter_z, daughter_a) && - get_nuc_decaybranchprob(nucindex, dectypeindex) > 0.) { + get_nuc_decaybranchprob(nucindex, decaytype) > 0.) { if (!a_isotopes.contains(nuc_a)) { a_isotopes.insert(nuc_a); // nuclide decays into correct atomic number but outside of the radionuclide list. Daughter is assumed diff --git a/decay.h b/decay.h index e2c1b1096..9818656ba 100644 --- a/decay.h +++ b/decay.h @@ -3,9 +3,11 @@ // #include +#include #include #include +#include "constants.h" #include "packet.h" namespace decay { @@ -18,7 +20,11 @@ enum decaytypes { DECAYTYPE_COUNT = 5, }; -void init_nuclides(std::vector zlist, std::vector alist); +constexpr std::array all_decaytypes = { + decaytypes::DECAYTYPE_ALPHA, decaytypes::DECAYTYPE_ELECTRONCAPTURE, decaytypes::DECAYTYPE_BETAPLUS, + decaytypes::DECAYTYPE_BETAMINUS, decaytypes::DECAYTYPE_NONE}; + +void init_nuclides(const std::vector &zlist, const std::vector &alist); int get_nucstring_z(const std::string &strnuc); int get_nucstring_a(const std::string &strnuc); int get_num_nuclides(); @@ -30,7 +36,6 @@ bool nuc_exists(int z, int a); double nucdecayenergygamma(int nucindex); double nucdecayenergygamma(int z, int a); void set_nucdecayenergygamma(int nucindex, double value); -double nucmass(int z, int a); void update_abundances(int modelgridindex, int timestep, double t_current); double get_endecay_per_ejectamass_t0_to_time_withexpansion(int modelgridindex, double tstart); double get_modelcell_simtime_endecay_per_mass(int mgi); @@ -42,6 +47,8 @@ double get_global_etot_t0_tinf(); void fprint_nuc_abundances(FILE *estimators_file, int modelgridindex, double t_current, int element); void setup_radioactive_pellet(double e0, int mgi, struct packet *pkt_ptr); void cleanup(); + +auto constexpr nucmass(int z, int a) -> double { return a * MH; } } // namespace decay #endif // DECAY_H diff --git a/exspec.cc b/exspec.cc index 7254c3f6a..2f9727f67 100644 --- a/exspec.cc +++ b/exspec.cc @@ -1,15 +1,3 @@ -/* 2007-10-30 -- MK - Non-grey treatment of UVOIR opacity as opacity_case 4 added. - Still not fully commented. - Comments are marked by /// Deactivated code by // */ -/* 2007-01-17 -- MK - Several minor modifications (some marked in the code with //MK), these include - - global printout() routine (located in sn3d.c) - - opacity_cases 2 and 3 added (changes in grid_init.c and update_grid.c, - original opacity stuff was moved there from input.c) */ -/* This is a code copied from Lucy 2004 paper on t-dependent supernova - explosions. */ - #include "exspec.h" #include @@ -30,48 +18,146 @@ FILE *output_file = nullptr; int tid = 0; bool use_cellhist = false; -bool neutral_flag = false; -gsl_rng *rng = nullptr; -std::mt19937_64 *stdrng = nullptr; +std::mt19937 stdrng(std::random_device{}()); gsl_integration_workspace *gslworkspace = nullptr; -auto main(int argc, char *argv[]) -> int { - const time_t sys_time_start = time(nullptr); +static void do_angle_bin(const int a, packet *pkts, bool load_allrank_packets, struct spec &rpkt_spectra, + struct spec &stokes_i, struct spec &stokes_q, struct spec &stokes_u, + struct spec &gamma_spectra) { + std::vector rpkt_light_curve_lum(globals::ntimesteps, 0.); + std::vector rpkt_light_curve_lumcmf(globals::ntimesteps, 0.); + std::vector gamma_light_curve_lum(globals::ntimesteps, 0.); + std::vector gamma_light_curve_lumcmf(globals::ntimesteps, 0.); + + /// Set up the spectrum grid and initialise the bins to zero. + init_spectra(rpkt_spectra, NU_MIN_R, NU_MAX_R, globals::do_emission_res); + + if constexpr (POL_ON) { + init_spectra(stokes_i, NU_MIN_R, NU_MAX_R, globals::do_emission_res); + init_spectra(stokes_q, NU_MIN_R, NU_MAX_R, globals::do_emission_res); + init_spectra(stokes_u, NU_MIN_R, NU_MAX_R, globals::do_emission_res); + } + + const double nu_min_gamma = 0.05 * MEV / H; + const double nu_max_gamma = 4. * MEV / H; + init_spectra(gamma_spectra, nu_min_gamma, nu_max_gamma, false); + + for (int p = 0; p < globals::nprocs_exspec; p++) { + struct packet *pkts_start = load_allrank_packets ? &pkts[p * globals::npkts] : pkts; + + if (a == -1 || !load_allrank_packets) { + char pktfilename[MAXFILENAMELENGTH]; + snprintf(pktfilename, MAXFILENAMELENGTH, "packets%.2d_%.4d.out", 0, p); + printout("reading %s (file %d of %d)\n", pktfilename, p + 1, globals::nprocs_exspec); + + if (access(pktfilename, F_OK) == 0) { + read_packets(pktfilename, pkts_start); + } else { + printout(" WARNING %s does not exist - trying temp packets file at beginning of timestep %d...\n", + pktfilename, globals::timestep_initial); + read_temp_packetsfile(globals::timestep_initial, p, pkts_start); + } + } #ifdef MPI_ON - MPI_Init(&argc, &argv); - MPI_Comm_rank(MPI_COMM_WORLD, &globals::rank_global); - MPI_Comm_size(MPI_COMM_WORLD, &globals::nprocs); - - MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, globals::rank_global, MPI_INFO_NULL, - &globals::mpi_comm_node); - // get the local rank within this node - MPI_Comm_rank(globals::mpi_comm_node, &globals::rank_in_node); - // get the number of ranks on the node - MPI_Comm_size(globals::mpi_comm_node, &globals::node_nprocs); - MPI_Barrier(MPI_COMM_WORLD); + MPI_Barrier(MPI_COMM_WORLD); +#endif - // make an inter-node communicator (using local rank as the key for group membership) - MPI_Comm_split(MPI_COMM_WORLD, globals::rank_in_node, globals::rank_global, &globals::mpi_comm_internode); + if (p % globals::nprocs != globals::rank_global) { + printout("skipping packets file %d %d\n", p + 1, globals::nprocs); + continue; + } - // take the node id from the local rank 0 (node master) and broadcast it - if (globals::rank_in_node == 0) { - MPI_Comm_rank(globals::mpi_comm_internode, &globals::node_id); - MPI_Comm_size(globals::mpi_comm_internode, &globals::node_count); + int nesc_tot = 0; + int nesc_gamma = 0; + int nesc_rpkt = 0; + for (int ii = 0; ii < globals::npkts; ii++) { + // printout("packet %d escape_type %d type %d", ii, pkts[ii].escape_type, pkts[ii].type); + if (pkts_start[ii].type == TYPE_ESCAPE) { + nesc_tot++; + if (pkts_start[ii].escape_type == TYPE_RPKT) { + nesc_rpkt++; + add_to_lc_res(&pkts_start[ii], a, rpkt_light_curve_lum, rpkt_light_curve_lumcmf); + add_to_spec_res(&pkts_start[ii], a, rpkt_spectra, POL_ON ? &stokes_i : nullptr, POL_ON ? &stokes_q : nullptr, + POL_ON ? &stokes_u : nullptr); + } else if (pkts_start[ii].escape_type == TYPE_GAMMA) { + nesc_gamma++; + if (a == -1) { + add_to_lc_res(&pkts_start[ii], a, gamma_light_curve_lum, gamma_light_curve_lumcmf); + add_to_spec_res(&pkts_start[ii], a, gamma_spectra, nullptr, nullptr, nullptr); + } + } + } + } + if (a == -1 || !load_allrank_packets) { + printout(" %d of %d packets escaped (%d gamma-pkts and %d r-pkts)\n", nesc_tot, globals::npkts, nesc_gamma, + nesc_rpkt); + } } - MPI_Bcast(&globals::node_id, 1, MPI_INT, 0, globals::mpi_comm_node); - MPI_Bcast(&globals::node_count, 1, MPI_INT, 0, globals::mpi_comm_node); - MPI_Barrier(MPI_COMM_WORLD); -#else - globals::rank_global = 0; - globals::nprocs = 1; - globals::rank_in_node = 0; - globals::node_nprocs = 1; - globals::node_id = 0; - globals::node_count = 0; + if (a == -1) { + // angle-averaged spectra and light curves + write_light_curve("light_curve.out", -1, rpkt_light_curve_lum, rpkt_light_curve_lumcmf, globals::ntimesteps); + write_light_curve("gamma_light_curve.out", -1, gamma_light_curve_lum, gamma_light_curve_lumcmf, + globals::ntimesteps); + + write_spectrum("spec.out", "emission.out", "emissiontrue.out", "absorption.out", rpkt_spectra, globals::ntimesteps); + + if constexpr (POL_ON) { + write_specpol("specpol.out", "emissionpol.out", "absorptionpol.out", &stokes_i, &stokes_q, &stokes_u); + } + + write_spectrum("gamma_spec.out", "", "", "", gamma_spectra, globals::ntimesteps); + + printout("finished angle-averaged stuff\n"); + } else { + // direction bin a + // line-of-sight dependent spectra and light curves + + char lc_filename[MAXFILENAMELENGTH] = ""; + snprintf(lc_filename, MAXFILENAMELENGTH, "light_curve_res_%.2d.out", a); + + char spec_filename[MAXFILENAMELENGTH] = ""; + snprintf(spec_filename, MAXFILENAMELENGTH, "spec_res_%.2d.out", a); + + char emission_filename[MAXFILENAMELENGTH] = ""; + snprintf(emission_filename, MAXFILENAMELENGTH, "emission_res_%.2d.out", a); + + char trueemission_filename[MAXFILENAMELENGTH] = ""; + snprintf(trueemission_filename, MAXFILENAMELENGTH, "emissiontrue_res_%.2d.out", a); + + char absorption_filename[MAXFILENAMELENGTH] = ""; + snprintf(absorption_filename, MAXFILENAMELENGTH, "absorption_res_%.2d.out", a); + + write_light_curve(lc_filename, a, rpkt_light_curve_lum, rpkt_light_curve_lumcmf, globals::ntimesteps); + write_spectrum(spec_filename, emission_filename, trueemission_filename, absorption_filename, rpkt_spectra, + globals::ntimesteps); + + if constexpr (POL_ON) { + char specpol_filename[MAXFILENAMELENGTH] = ""; + snprintf(specpol_filename, MAXFILENAMELENGTH, "specpol_res_%.2d.out", a); + + char emissionpol_filename[MAXFILENAMELENGTH] = ""; + snprintf(emissionpol_filename, MAXFILENAMELENGTH, "emissionpol_res_%.2d.out", a); + + char absorptionpol_filename[MAXFILENAMELENGTH] = ""; + snprintf(absorptionpol_filename, MAXFILENAMELENGTH, "absorptionpol_res_%.2d.out", a); + + write_specpol(specpol_filename, emissionpol_filename, absorptionpol_filename, &stokes_i, &stokes_q, &stokes_u); + } + + printout("Did %d of %d angle bins.\n", a + 1, MABINS); + } +} + +auto main(int argc, char *argv[]) -> int { + const time_t sys_time_start = time(nullptr); + +#ifdef MPI_ON + MPI_Init(&argc, &argv); #endif - char filename[MAXFILENAMELENGTH]; + + globals::setup_mpi_vars(); globals::startofline = std::make_unique(get_max_threads()); if (globals::rank_global == 0) { @@ -83,10 +169,13 @@ auto main(int argc, char *argv[]) -> int { MPI_Barrier(MPI_COMM_WORLD); #endif + char filename[MAXFILENAMELENGTH]; if (globals::rank_global == 0) { snprintf(filename, MAXFILENAMELENGTH, "exspec.txt"); output_file = fopen_required(filename, "w"); setvbuf(output_file, nullptr, _IOLBF, 1); + } else { + output_file = nullptr; } printout("git branch %s\n", GIT_BRANCH); @@ -123,187 +212,51 @@ auto main(int argc, char *argv[]) -> int { printout("time before input %ld\n", time(nullptr)); input(globals::rank_global); printout("time after input %ld\n", time(nullptr)); + // nprocs_exspec is the number of rank output files to process with expec // however, we might be running exspec with 1 or just a few ranks - globals::nprocs = globals::nprocs_exspec; - - constexpr double maxpktmem_mb = 16000; - bool load_allrank_packets = false; - if ((globals::nprocs_exspec * globals::npkts * sizeof(struct packet) / 1024. / 1024.) < maxpktmem_mb) { - printout( - "mem_usage: loading packets from all %d processes simultaneously (total %d packets, %.1f MB memory is within " - "limit of %.1f MB)\n", - globals::nprocs_exspec, globals::nprocs_exspec * globals::npkts, - globals::nprocs_exspec * globals::npkts * sizeof(struct packet) / 1024. / 1024., maxpktmem_mb); - load_allrank_packets = true; + auto *pkts = static_cast(malloc(globals::nprocs_exspec * globals::npkts * sizeof(struct packet))); + const bool load_allrank_packets = (pkts != nullptr); + if (load_allrank_packets) { + printout("mem_usage: loading %d packets from each %d processes simultaneously (total %d packets, %.1f MB memory)\n", + globals::npkts, globals::nprocs_exspec, globals::nprocs_exspec * globals::npkts, + globals::nprocs_exspec * globals::npkts * sizeof(struct packet) / 1024. / 1024.); } else { + printout("mem_usage: malloc failed to allocate memory for all packets\n"); printout( - "mem_usage: loading packets from each of %d processes sequentially (total %d packets, %.1f MB memory would be " - "above limit of %.1f MB)\n", - globals::nprocs_exspec, globals::nprocs_exspec * globals::npkts, - globals::nprocs_exspec * globals::npkts * sizeof(struct packet) / 1024. / 1024., maxpktmem_mb); - load_allrank_packets = false; + "mem_usage: loading %d packets from each of %d processes sequentially (total %d packets, %.1f MB memory)\n", + globals::npkts, globals::nprocs_exspec, globals::nprocs_exspec * globals::npkts, + globals::nprocs_exspec * globals::npkts * sizeof(struct packet) / 1024. / 1024.); + auto *pkts = static_cast(malloc(globals::npkts * sizeof(struct packet))); + assert_always(pkts != nullptr); } - const int npkts_loaded = load_allrank_packets ? globals::nprocs_exspec * globals::npkts : globals::npkts; - auto *pkts = static_cast(malloc(npkts_loaded * sizeof(struct packet))); - init_spectrum_trace(); // needed for TRACE_EMISSION_ABSORPTION_REGION_ON - auto rpkt_spectra = alloc_spectra(globals::do_emission_res); - - std::unique_ptr stokes_i = nullptr; - std::unique_ptr stokes_q = nullptr; - std::unique_ptr stokes_u = nullptr; + struct spec rpkt_spectra; - if constexpr (POL_ON) { - stokes_i = alloc_spectra(globals::do_emission_res); - stokes_q = alloc_spectra(globals::do_emission_res); - stokes_u = alloc_spectra(globals::do_emission_res); - } + struct spec stokes_i; + struct spec stokes_q; + struct spec stokes_u; - auto gamma_spectra = alloc_spectra(false); + struct spec gamma_spectra; - /// Initialise the grid. Call routine that sets up the initial positions - /// and sizes of the grid cells. - // grid_init(); time_init(); - const int amax = ((grid::get_model_type() == grid::RHO_1D_READ)) ? 0 : MABINS; + const int amax = ((grid::get_model_type() == GRID_SPHERICAL1D)) ? 0 : MABINS; // a is the escape direction angle bin for (int a = -1; a < amax; a++) { - /// Set up the light curve grid and initialise the bins to zero. - std::vector rpkt_light_curve_lum(globals::ntstep, 0.); - std::vector rpkt_light_curve_lumcmf(globals::ntstep, 0.); - std::vector gamma_light_curve_lum(globals::ntstep, 0.); - std::vector gamma_light_curve_lumcmf(globals::ntstep, 0.); - /// Set up the spectrum grid and initialise the bins to zero. - - init_spectra(*rpkt_spectra, NU_MIN_R, NU_MAX_R, globals::do_emission_res); - - if constexpr (POL_ON) { - init_spectra(*stokes_i, NU_MIN_R, NU_MAX_R, globals::do_emission_res); - init_spectra(*stokes_q, NU_MIN_R, NU_MAX_R, globals::do_emission_res); - init_spectra(*stokes_u, NU_MIN_R, NU_MAX_R, globals::do_emission_res); - } - - const double nu_min_gamma = 0.05 * MEV / H; - const double nu_max_gamma = 4. * MEV / H; - init_spectra(*gamma_spectra, nu_min_gamma, nu_max_gamma, false); - - for (int p = 0; p < globals::nprocs_exspec; p++) { - struct packet *pkts_start = load_allrank_packets ? &pkts[p * globals::npkts] : pkts; - - if (a == -1 || !load_allrank_packets) { - char pktfilename[MAXFILENAMELENGTH]; - snprintf(pktfilename, MAXFILENAMELENGTH, "packets%.2d_%.4d.out", 0, p); - printout("reading %s (file %d of %d)\n", pktfilename, p + 1, globals::nprocs_exspec); - - if (access(pktfilename, F_OK) == 0) { - read_packets(pktfilename, pkts_start); - } else { - printout(" WARNING %s does not exist - trying temp packets file at beginning of timestep %d...\n ", - pktfilename, globals::itstep); - read_temp_packetsfile(globals::itstep, p, pkts_start); - } - } - - int nesc_tot = 0; - int nesc_gamma = 0; - int nesc_rpkt = 0; - for (int ii = 0; ii < globals::npkts; ii++) { - // printout("packet %d escape_type %d type %d", ii, pkts[ii].escape_type, pkts[ii].type); - if (pkts_start[ii].type == TYPE_ESCAPE) { - nesc_tot++; - if (pkts_start[ii].escape_type == TYPE_RPKT) { - nesc_rpkt++; - add_to_lc_res(&pkts_start[ii], a, rpkt_light_curve_lum.data(), rpkt_light_curve_lumcmf.data()); - add_to_spec_res(&pkts_start[ii], a, *rpkt_spectra, stokes_i.get(), stokes_q.get(), stokes_u.get()); - } else if (pkts_start[ii].escape_type == TYPE_GAMMA) { - nesc_gamma++; - if (a == -1) { - add_to_lc_res(&pkts_start[ii], a, gamma_light_curve_lum.data(), gamma_light_curve_lumcmf.data()); - add_to_spec_res(&pkts_start[ii], a, *gamma_spectra, nullptr, nullptr, nullptr); - } - } - } - } - if (a == -1 || !load_allrank_packets) { - printout(" %d of %d packets escaped (%d gamma-pkts and %d r-pkts)\n", nesc_tot, globals::npkts, nesc_gamma, - nesc_rpkt); - } - } - - if (a == -1) { - // angle-averaged spectra and light curves - write_light_curve("light_curve.out", -1, rpkt_light_curve_lum.data(), rpkt_light_curve_lumcmf.data(), - globals::ntstep); - write_light_curve("gamma_light_curve.out", -1, gamma_light_curve_lum.data(), gamma_light_curve_lumcmf.data(), - globals::ntstep); - - write_spectrum("spec.out", "emission.out", "emissiontrue.out", "absorption.out", *rpkt_spectra, globals::ntstep); - - if constexpr (POL_ON) { - write_specpol("specpol.out", "emissionpol.out", "absorptionpol.out", stokes_i.get(), stokes_q.get(), - stokes_u.get()); - } - - write_spectrum("gamma_spec.out", nullptr, nullptr, nullptr, *gamma_spectra, globals::ntstep); - - printout("finished angle-averaged stuff\n"); - } else { - // direction bin a - // line-of-sight dependent spectra and light curves - - char lc_filename[MAXFILENAMELENGTH] = ""; - snprintf(lc_filename, MAXFILENAMELENGTH, "light_curve_res_%.2d.out", a); - - char spec_filename[MAXFILENAMELENGTH] = ""; - snprintf(spec_filename, MAXFILENAMELENGTH, "spec_res_%.2d.out", a); - - char emission_filename[MAXFILENAMELENGTH] = ""; - snprintf(emission_filename, MAXFILENAMELENGTH, "emission_res_%.2d.out", a); - - char trueemission_filename[MAXFILENAMELENGTH] = ""; - snprintf(trueemission_filename, MAXFILENAMELENGTH, "emissiontrue_res_%.2d.out", a); - - char absorption_filename[MAXFILENAMELENGTH] = ""; - snprintf(absorption_filename, MAXFILENAMELENGTH, "absorption_res_%.2d.out", a); - - write_light_curve(lc_filename, a, rpkt_light_curve_lum.data(), rpkt_light_curve_lumcmf.data(), globals::ntstep); - write_spectrum(spec_filename, emission_filename, trueemission_filename, absorption_filename, *rpkt_spectra, - globals::ntstep); - - if constexpr (POL_ON) { - char specpol_filename[MAXFILENAMELENGTH] = ""; - snprintf(specpol_filename, MAXFILENAMELENGTH, "specpol_res_%.2d.out", a); - - char emissionpol_filename[MAXFILENAMELENGTH] = ""; - snprintf(emissionpol_filename, MAXFILENAMELENGTH, "emissionpol_res_%.2d.out", a); - - char absorptionpol_filename[MAXFILENAMELENGTH] = ""; - snprintf(absorptionpol_filename, MAXFILENAMELENGTH, "absorptionpol_res_%.2d.out", a); - - write_specpol(specpol_filename, emissionpol_filename, absorptionpol_filename, stokes_i.get(), stokes_q.get(), - stokes_u.get()); - } - - printout("Did %d of %d angle bins.\n", a + 1, MABINS); - } + do_angle_bin(a, pkts, load_allrank_packets, rpkt_spectra, stokes_i, stokes_q, stokes_u, gamma_spectra); } - rpkt_spectra.reset(); - stokes_i.reset(); - stokes_q.reset(); - stokes_u.reset(); - gamma_spectra.reset(); - free(pkts); decay::cleanup(); - printout("exspec finished at %ld (tstart + %ld seconds)\n", time(nullptr), time(nullptr) - sys_time_start); - fclose(output_file); + + if (output_file != nullptr) { + fclose(output_file); + } #ifdef MPI_ON MPI_Finalize(); diff --git a/gammapkt.cc b/gammapkt.cc index e8b9dd377..545fb711a 100644 --- a/gammapkt.cc +++ b/gammapkt.cc @@ -8,7 +8,6 @@ #include #include -#include "boundary.h" #include "decay.h" #include "grid.h" #include "nonthermal.h" @@ -201,13 +200,13 @@ void init_gamma_linelist() { } void normalise_grey(int nts) { - const double dt = globals::time_step[nts].width; - globals::time_step[nts].gamma_dep_pathint = 0.; + const double dt = globals::timesteps[nts].width; + globals::timesteps[nts].gamma_dep_pathint = 0.; for (int mgi = 0; mgi < grid::get_npts_model(); mgi++) { if (grid::get_numassociatedcells(mgi) > 0) { - const double dV = grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::time_step[nts].mid / globals::tmin, 3); + const double dV = grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::timesteps[nts].mid / globals::tmin, 3); - globals::time_step[nts].gamma_dep_pathint += globals::rpkt_emiss[mgi] / globals::nprocs; + globals::timesteps[nts].gamma_dep_pathint += globals::rpkt_emiss[mgi] / globals::nprocs; globals::rpkt_emiss[mgi] = globals::rpkt_emiss[mgi] * ONEOVER4PI / dV / dt / globals::nprocs; @@ -221,7 +220,7 @@ static void choose_gamma_ray(struct packet *pkt_ptr) { // Routine to choose which gamma ray line it'll be. const int nucindex = pkt_ptr->pellet_nucindex; - double const E_gamma = decay::nucdecayenergygamma(nucindex); // Average energy per gamma line of a decay + const double E_gamma = decay::nucdecayenergygamma(nucindex); // Average energy per gamma line of a decay const double zrand = rng_uniform(); int nselected = -1; @@ -283,20 +282,18 @@ void pellet_gamma_decay(struct packet *pkt_ptr) { // that it's now a gamma ray. pkt_ptr->prop_time = pkt_ptr->tdecay; - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_rf = pkt_ptr->nu_cmf / dopplerfactor; pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; pkt_ptr->type = TYPE_GAMMA; - pkt_ptr->last_cross = NONE; + pkt_ptr->last_cross = BOUNDARY_NONE; // initialise polarisation information pkt_ptr->stokes[0] = 1.0; pkt_ptr->stokes[1] = pkt_ptr->stokes[2] = 0.0; - double dummy_dir[3]; + std::array dummy_dir = {0., 0., 1.}; - dummy_dir[0] = dummy_dir[1] = 0.0; - dummy_dir[2] = 1.0; cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); if ((dot(pkt_ptr->pol_dir, pkt_ptr->pol_dir)) < 1.e-8) { dummy_dir[0] = dummy_dir[2] = 0.0; @@ -322,35 +319,34 @@ constexpr auto sigma_compton_partial(const double x, const double f) -> double return (3 * SIGMA_T * (term1 + term2 + term3) / (8 * x)); } -static auto sig_comp(const struct packet *pkt_ptr) -> double { +static auto get_chi_compton_rf(const struct packet *pkt_ptr) -> double { + // calculate the absorption coefficient [cm^-1] for Compton scattering in the observer reference frame // Start by working out the compton x-section in the co-moving frame. - double const xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; + const double xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; // Use this to decide whether the Thompson limit is acceptable. - double sigma_cmf = NAN; + double sigma_cmf; if (xx < THOMSON_LIMIT) { sigma_cmf = SIGMA_T; } else { - double const fmax = (1 + (2 * xx)); + const double fmax = (1 + (2 * xx)); sigma_cmf = sigma_compton_partial(xx, fmax); } // Now need to multiply by the electron number density. - const int cellindex = pkt_ptr->where; - sigma_cmf *= grid::get_nnetot(grid::get_cell_modelgridindex(cellindex)); - - // Now need to convert between frames. + const double chi_cmf = sigma_cmf * grid::get_nnetot(grid::get_cell_modelgridindex(pkt_ptr->where)); - const double sigma_rf = sigma_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr); + // convert between frames + const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); - assert_testmodeonly(std::isfinite(sigma_rf)); + assert_testmodeonly(std::isfinite(chi_rf)); - return sigma_rf; + return chi_rf; } -static auto choose_f(double xx, double zrand) -> double +static auto choose_f(const double xx, const double zrand) -> double // To choose the value of f to integrate to - idea is we want // sigma_compton_partial(xx,f) = zrand. { @@ -472,7 +468,7 @@ static void compton_scatter(struct packet *pkt_ptr) const double cos_theta = (xx < THOMSON_LIMIT) ? thomson_angle() : 1. - ((f - 1) / xx); - double new_dir[3]; + double new_dir[3] = {NAN, NAN, NAN}; scatter_dir(cmf_dir, cos_theta, new_dir); const double test = dot(new_dir, new_dir); @@ -504,11 +500,11 @@ static void compton_scatter(struct packet *pkt_ptr) // It now has a rest frame direction and a co-moving frequency. // Just need to set the rest frame energy. - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_rf = pkt_ptr->nu_cmf / dopplerfactor; pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; - pkt_ptr->last_cross = NONE; // allow it to re-cross a boundary + pkt_ptr->last_cross = BOUNDARY_NONE; // allow it to re-cross a boundary } else { // It's converted to an e-minus packet. pkt_ptr->type = TYPE_NTLEPTON; @@ -517,126 +513,124 @@ static void compton_scatter(struct packet *pkt_ptr) } } -static auto sig_photo_electric(const struct packet *pkt_ptr) -> double { - // photo electric effect scattering +static auto get_chi_photo_electric_rf(const struct packet *pkt_ptr) -> double { + // calculate the absorption coefficient [cm^-1] for photo electric effect scattering in the observer reference frame - double sigma_cmf = NAN; + double chi_cmf = NAN; // Start by working out the x-section in the co-moving frame. const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); const double rho = grid::get_rho(mgi); - if (globals::gamma_grey < 0) { - // double sigma_cmf_cno = 0.0448e-24 * pow(pkt_ptr->nu_cmf / 2.41326e19, -3.2); + if (globals::gamma_kappagrey < 0) { + // Cross sections from Equation 2 of Ambwani & Sutherland (1988), attributed to Veigele (1973) - double sigma_cmf_si = 1.16e-24 * pow(pkt_ptr->nu_cmf / 2.41326e19, -3.13); + // 2.41326e19 Hz = 100 keV / H + const double hnu_over_100kev = pkt_ptr->nu_cmf / 2.41326e+19; - double sigma_cmf_fe = 25.7e-24 * pow(pkt_ptr->nu_cmf / 2.41326e19, -3.0); + // double sigma_cmf_cno = 0.0448e-24 * pow(hnu_over_100kev, -3.2); - // 2.41326e19 = 100keV in frequency. + const double sigma_cmf_si = 1.16e-24 * pow(hnu_over_100kev, -3.13); - // Now need to multiply by the particle number density. + const double sigma_cmf_fe = 25.7e-24 * pow(hnu_over_100kev, -3.0); - // sigma_cmf_cno *= rho * (1. - f_fe) / MH / 14; - // Assumes Z = 7. So mass = 14. + // Now need to multiply by the particle number density. - sigma_cmf_si *= rho / MH / 28; + const double chi_cmf_si = sigma_cmf_si * (rho / MH / 28); // Assumes Z = 14. So mass = 28. - sigma_cmf_fe *= rho / MH / 56; + const double chi_cmf_fe = sigma_cmf_fe * (rho / MH / 56); // Assumes Z = 28. So mass = 56. const double f_fe = grid::get_ffegrp(mgi); - sigma_cmf = (sigma_cmf_fe * f_fe) + (sigma_cmf_si * (1. - f_fe)); + chi_cmf = (chi_cmf_fe * f_fe) + (chi_cmf_si * (1. - f_fe)); } else { - sigma_cmf = globals::gamma_grey * rho; + chi_cmf = globals::gamma_kappagrey * rho; } - // Now need to convert between frames. + // Now convert between frames. - const double sigma_rf = sigma_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr); - return sigma_rf; + const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + return chi_rf; } -static auto sig_pair_prod(const struct packet *pkt_ptr) -> double { - // Cross section for pair production. +static auto sigma_pair_prod_rf(const struct packet *pkt_ptr) -> double { + // calculate the absorption coefficient [cm^-1] for pair production in the observer reference frame - double sigma_cmf = NAN; + const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); + const double rho = grid::get_rho(mgi); - // Start by working out the x-section in the co-moving frame. + if (globals::gamma_kappagrey >= 0.) { + return 0.; + } - const int cellindex = pkt_ptr->where; - const int mgi = grid::get_cell_modelgridindex(cellindex); - const double rho = grid::get_rho(mgi); + // 2.46636e+20 Hz = 1022 keV / H + if (pkt_ptr->nu_cmf <= 2.46636e+20) { + return 0.; + } - if (globals::gamma_grey < 0) { - // 2.46636e+20 = 1022 keV in frequency - // 3.61990e+20 = 1500 keV in frequency + // double sigma_cmf_cno; + double sigma_cmf_si = NAN; + double sigma_cmf_fe = NAN; + const double f_fe = grid::get_ffegrp(mgi); - if (pkt_ptr->nu_cmf > 2.46636e+20) { - // double sigma_cmf_cno; - double sigma_cmf_si = NAN; - double sigma_cmf_fe = NAN; - const double f_fe = grid::get_ffegrp(mgi); - if (pkt_ptr->nu_cmf > 3.61990e+20) { - // sigma_cmf_cno = (0.0481 + (0.301 * ((pkt_ptr->nu_cmf/2.41326e+20) - 1.5))) * 49.e-27; + // Cross sections from Equation 2 of Ambwani & Sutherland (1988), attributed to Hubbell (1969) - sigma_cmf_si = (0.0481 + (0.301 * ((pkt_ptr->nu_cmf / 2.41326e+20) - 1.5))) * 196.e-27; + // 3.61990e+20 = 1500 keV in frequency / H + const double hnu_over_mev = pkt_ptr->nu_cmf / 2.41326e+20; + if (pkt_ptr->nu_cmf > 3.61990e+20) { + // sigma_cmf_cno = (0.0481 + (0.301 * (hnu_over_mev - 1.5))) * 49.e-27; - sigma_cmf_fe = (0.0481 + (0.301 * ((pkt_ptr->nu_cmf / 2.41326e+20) - 1.5))) * 784.e-27; - } else { - // sigma_cmf_cno = 1.0063 * ((pkt_ptr->nu_cmf/2.41326e+20) - 1.022) * 49.e-27; + sigma_cmf_si = (0.0481 + (0.301 * (hnu_over_mev - 1.5))) * 196.e-27; - sigma_cmf_si = 1.0063 * ((pkt_ptr->nu_cmf / 2.41326e+20) - 1.022) * 196.e-27; + sigma_cmf_fe = (0.0481 + (0.301 * (hnu_over_mev - 1.5))) * 784.e-27; + } else { + // sigma_cmf_cno = 1.0063 * (hnu_over_mev - 1.022) * 49.e-27; - sigma_cmf_fe = 1.0063 * ((pkt_ptr->nu_cmf / 2.41326e+20) - 1.022) * 784.e-27; - } + sigma_cmf_si = 1.0063 * (hnu_over_mev - 1.022) * 196.e-27; - // Now need to multiply by the particle number density. + sigma_cmf_fe = 1.0063 * (hnu_over_mev - 1.022) * 784.e-27; + } - // sigma_cmf_cno *= rho * (1. - f_fe) / MH / 14; - // Assumes Z = 7. So mass = 14. + // multiply by the particle number density. - sigma_cmf_si *= rho / MH / 28; - // Assumes Z = 14. So mass = 28. + // sigma_cmf_cno *= rho * (1. - f_fe) / MH / 14; + // Assumes Z = 7. So mass = 14. - sigma_cmf_fe *= rho / MH / 56; - // Assumes Z = 28. So mass = 56. + const double chi_cmf_si = sigma_cmf_si * (rho / MH / 28); + // Assumes Z = 14. So mass = 28. - sigma_cmf = (sigma_cmf_fe * f_fe) + (sigma_cmf_si * (1. - f_fe)); - } else { - sigma_cmf = 0.0; - } - } else { - sigma_cmf = 0.0; - } + const double chi_cmf_fe = sigma_cmf_fe * (rho / MH / 56); + // Assumes Z = 28. So mass = 56. + + const double chi_cmf = (chi_cmf_fe * f_fe) + (chi_cmf_si * (1. - f_fe)); // Now need to convert between frames. - double sigma_rf = sigma_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr); + double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); - if (sigma_rf < 0) { - printout("Negative pair production sigma. Setting to zero. Abort? %g\n", sigma_rf); - sigma_rf = 0.0; + if (chi_rf < 0) { + printout("Negative pair production sigma. Setting to zero. Abort? %g\n", chi_rf); + chi_rf = 0.0; } - return sigma_rf; + return chi_rf; } constexpr auto meanf_sigma(const double x) -> double // Routine to compute the mean energy converted to non-thermal electrons times // the Klein-Nishina cross section. { - double const f = 1 + (2 * x); + const double f = 1 + (2 * x); - double const term0 = 2 / x; - double const term1 = (1 - (2 / x) - (3 / (x * x))) * log(f); - double const term2 = ((4 / x) + (3 / (x * x)) - 1) * 2 * x / f; - double const term3 = (1 - (2 / x) - (1 / (x * x))) * 2 * x * (1 + x) / f / f; - double const term4 = -2. * x * ((4 * x * x) + (6 * x) + 3) / 3 / f / f / f; + const double term0 = 2 / x; + const double term1 = (1 - (2 / x) - (3 / (x * x))) * log(f); + const double term2 = ((4 / x) + (3 / (x * x)) - 1) * 2 * x / f; + const double term3 = (1 - (2 / x) - (1 / (x * x))) * 2 * x * (1 + x) / f / f; + const double term4 = -2. * x * ((4 * x * x) + (6 * x) + 3) / 3 / f / f / f; - double const tot = 3 * SIGMA_T * (term0 + term1 + term2 + term3 + term4) / (8 * x); + const double tot = 3 * SIGMA_T * (term0 + term1 + term2 + term3 + term4) / (8 * x); return tot; } @@ -655,33 +649,30 @@ static void rlc_emiss_gamma(const struct packet *pkt_ptr, const double dist) { // Called with a packet that is about to travel a // distance dist in the lab frame. - const int cellindex = pkt_ptr->where; - const int mgi = grid::get_cell_modelgridindex(cellindex); - - if (dist > 0) { - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); - - double const doppler_sq = doppler_squared_nucmf_on_nurf(pkt_ptr->dir, vel_vec); - - const double xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; - double heating_cont = ((meanf_sigma(xx) * grid::get_nnetot(mgi)) + sig_photo_electric(pkt_ptr) + - (sig_pair_prod(pkt_ptr) * (1. - (2.46636e+20 / pkt_ptr->nu_cmf)))); - heating_cont = heating_cont * pkt_ptr->e_rf * dist * doppler_sq; + if (!(dist > 0)) { + return; + } - // The terms in the above are for Compton, photoelectric and pair production. The pair production one - // assumes that a fraction (1. - (1.022 MeV / nu)) of the gamma's energy is thermalised. - // The remaining 1.022 MeV is made into gamma rays + const double doppler_sq = doppler_squared_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); - // For normalisation this needs to be - // 1) divided by volume - // 2) divided by the length of the time step - // 3) divided by 4 pi sr - // This will all be done later - assert_testmodeonly(heating_cont >= 0.); - assert_testmodeonly(isfinite(heating_cont)); - safeadd(globals::rpkt_emiss[mgi], heating_cont); - } + const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); + const double xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; + double heating_cont = ((meanf_sigma(xx) * grid::get_nnetot(mgi)) + get_chi_photo_electric_rf(pkt_ptr) + + (sigma_pair_prod_rf(pkt_ptr) * (1. - (2.46636e+20 / pkt_ptr->nu_cmf)))); + heating_cont = heating_cont * pkt_ptr->e_rf * dist * doppler_sq; + + // The terms in the above are for Compton, photoelectric and pair production. The pair production one + // assumes that a fraction (1. - (1.022 MeV / nu)) of the gamma's energy is thermalised. + // The remaining 1.022 MeV is made into gamma rays + + // For normalisation this needs to be + // 1) divided by volume + // 2) divided by the length of the time step + // 3) divided by 4 pi sr + // This will all be done later + assert_testmodeonly(heating_cont >= 0.); + assert_testmodeonly(isfinite(heating_cont)); + safeadd(globals::rpkt_emiss[mgi], heating_cont); } void pair_prod(struct packet *pkt_ptr) { @@ -727,12 +718,12 @@ void pair_prod(struct packet *pkt_ptr) { angle_ab(dir_cmf, vel_vec, pkt_ptr->dir); - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_rf = pkt_ptr->nu_cmf / dopplerfactor; pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; pkt_ptr->type = TYPE_GAMMA; - pkt_ptr->last_cross = NONE; + pkt_ptr->last_cross = BOUNDARY_NONE; } } @@ -752,11 +743,12 @@ void do_gamma(struct packet *pkt_ptr, double t2) // grid cell into which we pass. int snext = 0; - double sdist = boundary_cross(pkt_ptr, &snext); + double sdist = grid::boundary_distance(pkt_ptr->dir, pkt_ptr->pos, pkt_ptr->prop_time, pkt_ptr->where, &snext, + &pkt_ptr->last_cross); - const double maxsdist = (GRID_TYPE == GRID_SPHERICAL1D) - ? 2 * globals::rmax * (pkt_ptr->prop_time + sdist / CLIGHT_PROP) / globals::tmin - : globals::rmax * pkt_ptr->prop_time / globals::tmin; + const double maxsdist = (GRID_TYPE == GRID_CARTESIAN3D) + ? globals::rmax * pkt_ptr->prop_time / globals::tmin + : 2 * globals::rmax * (pkt_ptr->prop_time + sdist / CLIGHT_PROP) / globals::tmin; if (sdist > maxsdist) { printout("Unreasonably large sdist (gamma). Abort. %g %g %g\n", globals::rmax, pkt_ptr->prop_time / globals::tmin, sdist); @@ -783,22 +775,22 @@ void do_gamma(struct packet *pkt_ptr, double t2) // Compton scattering - need to determine the scattering co-efficient. // Routine returns the value in the rest frame. - double kap_compton = 0.0; - if (globals::gamma_grey < 0) { - kap_compton = sig_comp(pkt_ptr); + double chi_compton = 0.0; + if (globals::gamma_kappagrey < 0) { + chi_compton = get_chi_compton_rf(pkt_ptr); } - const double kap_photo_electric = sig_photo_electric(pkt_ptr); - const double kap_pair_prod = sig_pair_prod(pkt_ptr); - const double kap_tot = kap_compton + kap_photo_electric + kap_pair_prod; + const double chi_photo_electric = get_chi_photo_electric_rf(pkt_ptr); + const double chi_pair_prod = sigma_pair_prod_rf(pkt_ptr); + const double chi_tot = chi_compton + chi_photo_electric + chi_pair_prod; - assert_testmodeonly(std::isfinite(kap_compton)); - assert_testmodeonly(std::isfinite(kap_photo_electric)); - assert_testmodeonly(std::isfinite(kap_pair_prod)); + assert_testmodeonly(std::isfinite(chi_compton)); + assert_testmodeonly(std::isfinite(chi_photo_electric)); + assert_testmodeonly(std::isfinite(chi_pair_prod)); // So distance before physical event is... - double const edist = (tau_next - tau_current) / kap_tot; + const double edist = (tau_next - tau_current) / chi_tot; if (edist < 0) { printout("Negative distance (edist). Abort. \n"); @@ -807,7 +799,7 @@ void do_gamma(struct packet *pkt_ptr, double t2) // Find how far it can travel during the time inverval. - double const tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; + const double tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; if (tdist < 0) { printout("Negative distance (tdist). Abort. \n"); @@ -821,7 +813,7 @@ void do_gamma(struct packet *pkt_ptr, double t2) move_pkt(pkt_ptr, sdist / 2.); // Move it into the new cell. - if (kap_tot > 0) { + if (chi_tot > 0) { rlc_emiss_gamma(pkt_ptr, sdist); } @@ -829,14 +821,14 @@ void do_gamma(struct packet *pkt_ptr, double t2) move_pkt(pkt_ptr, sdist / 2.); if (snext != pkt_ptr->where) { - change_cell(pkt_ptr, snext); + grid::change_cell(pkt_ptr, snext); } } else if ((tdist < sdist) && (tdist < edist)) { // Doesn't reach boundary. pkt_ptr->prop_time += tdist / 2. / CLIGHT_PROP; move_pkt(pkt_ptr, tdist / 2.); - if (kap_tot > 0) { + if (chi_tot > 0) { rlc_emiss_gamma(pkt_ptr, tdist); } pkt_ptr->prop_time = t2; @@ -844,7 +836,7 @@ void do_gamma(struct packet *pkt_ptr, double t2) } else if ((edist < sdist) && (edist < tdist)) { pkt_ptr->prop_time += edist / 2. / CLIGHT_PROP; move_pkt(pkt_ptr, edist / 2.); - if (kap_tot > 0) { + if (chi_tot > 0) { rlc_emiss_gamma(pkt_ptr, edist); } pkt_ptr->prop_time += edist / 2. / CLIGHT_PROP; @@ -852,24 +844,24 @@ void do_gamma(struct packet *pkt_ptr, double t2) // event occurs. Choose which event and call the appropriate subroutine. zrand = rng_uniform(); - if (kap_compton > (zrand * kap_tot)) { + if (chi_compton > (zrand * chi_tot)) { // Compton scattering. compton_scatter(pkt_ptr); - } else if ((kap_compton + kap_photo_electric) > (zrand * kap_tot)) { + } else if ((chi_compton + chi_photo_electric) > (zrand * chi_tot)) { // Photo electric effect - makes it a k-packet for sure. pkt_ptr->type = TYPE_NTLEPTON; pkt_ptr->absorptiontype = -4; // pkt_ptr->type = TYPE_PRE_KPKT; stats::increment(stats::COUNTER_NT_STAT_FROM_GAMMA); - } else if ((kap_compton + kap_photo_electric + kap_pair_prod) > (zrand * kap_tot)) { + } else if ((chi_compton + chi_photo_electric + chi_pair_prod) > (zrand * chi_tot)) { // It's a pair production pair_prod(pkt_ptr); } else { - printout("Failed to identify event. Gamma (1). kap_compton %g kap_photo_electric %g kap_tot %g zrand %g Abort.\n", - kap_compton, kap_photo_electric, kap_tot, zrand); + printout("Failed to identify event. Gamma (1). chi_compton %g chi_photo_electric %g chi_tot %g zrand %g Abort.\n", + chi_compton, chi_photo_electric, chi_tot, zrand); const int cellindex = pkt_ptr->where; printout( - " /*globals::cell[*/pkt_ptr->where].rho %g pkt_ptr->nu_cmf %g pkt_ptr->dir[0] %g pkt_ptr->dir[1] %g " + " globals::cell[pkt_ptr->where].rho %g pkt_ptr->nu_cmf %g pkt_ptr->dir[0] %g pkt_ptr->dir[1] %g " "pkt_ptr->dir[2] %g pkt_ptr->pos[0] %g pkt_ptr->pos[1] %g pkt_ptr->pos[2] %g \n", grid::get_rho(grid::get_cell_modelgridindex(cellindex)), pkt_ptr->nu_cmf, pkt_ptr->dir[0], pkt_ptr->dir[0], pkt_ptr->dir[1], pkt_ptr->dir[2], pkt_ptr->pos[1], pkt_ptr->pos[2]); diff --git a/globals.cc b/globals.cc index d89a90c71..0e0fa01fb 100644 --- a/globals.cc +++ b/globals.cc @@ -9,7 +9,7 @@ namespace globals { double syn_dir[3]; // vector pointing from origin to observer -struct time *time_step = nullptr; +std::unique_ptr timesteps = nullptr; double *rpkt_emiss = nullptr; /// Volume estimator for the rpkt emissivity @@ -37,8 +37,8 @@ bool do_emission_res = true; std::unique_ptr startofline; -double gamma_grey; // set to -ve for proper treatment. If possitive, then - // gamma_rays are treated as grey with this opacity. +double gamma_kappagrey; // set to -ve for proper treatment. If possitive, then + // gamma_rays are treated as grey with this opacity. double max_path_step; @@ -50,20 +50,14 @@ int opacity_case; // 0 normally, 1 for Fe-grp dependence. /// ATOMIC DATA int nlines = -1; -struct elementlist_entry *elements = nullptr; +std::vector elements; const struct linelist_entry *linelist = nullptr; struct bflist_t *bflist = nullptr; -double *spontrecombcoeff = nullptr; - -// for USE_LUT_PHOTOION = true -double *corrphotoioncoeff = nullptr; // for USE_LUT_BFHEATING = true double *bfheating_coeff = nullptr; -double *bfcooling_coeff = nullptr; - -struct rpkt_cont_opacity *kappa_rpkt_cont = nullptr; +struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont = nullptr; /// Coolinglist int ncoolingterms; @@ -99,18 +93,17 @@ int node_count = -1; // number of MPI nodes int node_id = -1; // unique number for each node constexpr int npkts = MPKTS; -int nesc = 0; // number of packets that escape during current timestep +std::atomic nesc = 0; // number of packets that escape during current timestep -double coordmax[3]; double vmax; double rmax; /// Total mass and outer velocity/radius double tmax = -1.; /// End time of current simulation double tmin = -1.; /// Start time of current simulation -int ntstep = -1; /// Number of timesteps -int itstep = -1; /// Initial timestep's number -int ftstep = -1; /// Final timestep's number -int nts_global = -1; /// Current time step +int ntimesteps = -1; /// Number of timesteps +int timestep_initial = -1; /// Initial timestep's number +int timestep_finish = -1; /// Final timestep's number +int timestep = -1; /// Current time step /// New variables for other opacity cases, still grey. double opcase3_normal; /// MK: normalisation factor for opacity_case 3 @@ -119,16 +112,50 @@ double rho_crit; /// MK: critical opacity in opacity_case 3 (could now be int total_nlte_levels; /// total number of nlte levels -bool homogeneous_abundances; - bool simulation_continued_from_saved; double nu_rfcut; int num_lte_timesteps; double cell_is_optically_thick; int num_grey_timesteps; int n_titer; -bool initial_iteration; +bool lte_iteration; int n_kpktdiffusion_timesteps; float kpktdiffusion_timescale; +void setup_mpi_vars() { +#ifdef MPI_ON + MPI_Comm_rank(MPI_COMM_WORLD, &globals::rank_global); + MPI_Comm_size(MPI_COMM_WORLD, &globals::nprocs); + + // make an intra-node communicator (group ranks that can share memory) + MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, globals::rank_global, MPI_INFO_NULL, + &globals::mpi_comm_node); + // get the local rank within this node + MPI_Comm_rank(globals::mpi_comm_node, &globals::rank_in_node); + // get the number of ranks on the node + MPI_Comm_size(globals::mpi_comm_node, &globals::node_nprocs); + MPI_Barrier(MPI_COMM_WORLD); + + // make an inter-node communicator (using local rank as the key for group membership) + MPI_Comm_split(MPI_COMM_WORLD, globals::rank_in_node, globals::rank_global, &globals::mpi_comm_internode); + + // take the node id from the local rank 0 (node master) and broadcast it + if (globals::rank_in_node == 0) { + MPI_Comm_rank(globals::mpi_comm_internode, &globals::node_id); + MPI_Comm_size(globals::mpi_comm_internode, &globals::node_count); + } + + MPI_Bcast(&globals::node_id, 1, MPI_INT, 0, globals::mpi_comm_node); + MPI_Bcast(&globals::node_count, 1, MPI_INT, 0, globals::mpi_comm_node); + +#else + globals::rank_global = 0; + globals::nprocs = 1; + globals::rank_in_node = 0; + globals::node_nprocs = 1; + globals::node_id = 0; + globals::node_count = 0; +#endif +} + } // namespace globals \ No newline at end of file diff --git a/globals.h b/globals.h index 49c02dd6c..d9cf9f6a5 100644 --- a/globals.h +++ b/globals.h @@ -1,6 +1,7 @@ #ifndef GLOBALS_H #define GLOBALS_H +#include #include #include @@ -9,28 +10,27 @@ #endif #include "artisoptions.h" -#include "boundary.h" struct time { - double start; // time at start of this timestep. [s] - double width; // Width of timestep. [s] - double mid; // Mid time in step - computed logarithmically. [s] - double gamma_dep; // cmf gamma ray energy deposition from absorption events [erg] - double gamma_dep_pathint; // cmf gamma ray energy deposition from packet trajectories [erg] - double positron_dep; // cmf positron energy deposition [erg] - double eps_positron_ana_power; // cmf positron KE energy generation rate analytical [erg/s] - double electron_dep; // cmf electron energy deposition [erg] - double electron_emission; // cmf electron KE energy generation [erg] - double eps_electron_ana_power; // cmf electron KE energy generation rate analytical [erg/s] - double alpha_dep; // cmf alpha energy deposition [erg] - double alpha_emission; // cmf alpha KE energy generation [erg] - double eps_alpha_ana_power; // cmf alpha KE energy generation rate analytical [erg/s] - double gamma_emission; // gamma decay energy generation in this timestep [erg] - double qdot_betaminus; // energy generation from beta-minus decays (including neutrinos) [erg/s/g] - double qdot_alpha; // energy generation from alpha decays (including neutrinos) [erg/s/g] - double qdot_total; // energy generation from all decays (including neutrinos) [erg/s/g] - double cmf_lum; // cmf luminosity light curve [erg] - int pellet_decays; // Number of pellets that decay in this time step. + double start; // time at start of this timestep. [s] + double width; // Width of timestep. [s] + double mid; // Mid time in step - computed logarithmically. [s] + double gamma_dep; // cmf gamma ray energy deposition from absorption events [erg] + double gamma_dep_pathint; // cmf gamma ray energy deposition from packet trajectories [erg] + double positron_dep; // cmf positron energy deposition [erg] + double eps_positron_ana_power; // cmf positron KE energy generation rate analytical [erg/s] + double electron_dep; // cmf electron energy deposition [erg] + double electron_emission; // cmf electron KE energy generation [erg] + double eps_electron_ana_power; // cmf electron KE energy generation rate analytical [erg/s] + double alpha_dep; // cmf alpha energy deposition [erg] + double alpha_emission; // cmf alpha KE energy generation [erg] + double eps_alpha_ana_power; // cmf alpha KE energy generation rate analytical [erg/s] + double gamma_emission; // gamma decay energy generation in this timestep [erg] + double qdot_betaminus; // energy generation from beta-minus decays (including neutrinos) [erg/s/g] + double qdot_alpha; // energy generation from alpha decays (including neutrinos) [erg/s/g] + double qdot_total; // energy generation from all decays (including neutrinos) [erg/s/g] + double cmf_lum; // cmf luminosity light curve [erg] + std::atomic pellet_decays; // Number of pellets that decay in this time step. }; struct bflist_t { @@ -62,7 +62,7 @@ struct groundphixslist { struct phixslist { double *groundcont_gamma_contr = nullptr; // for either USE_LUT_PHOTOION = true or !USE_LUT_BFHEATING = false - double *kappa_bf_sum = nullptr; + double *chi_bf_sum = nullptr; double *gamma_contr = nullptr; // needed for DETAILED_BF_ESTIMATORS_ON }; @@ -127,6 +127,7 @@ struct elementlist_entry { /// and their daughters. Neither it will work with OpenMP threads. float abundance; /// float initstablemeannucmass; /// Atomic mass number in multiple of MH + bool has_nlte_levels; }; struct linelist_entry { @@ -139,26 +140,22 @@ struct linelist_entry { int lowerlevelindex; /// and lower levels }; -struct nne_solution_paras { - int cellnumber; -}; - struct gslintegration_paras { const double nu_edge; const float T; const float *const photoion_xs; }; -struct rpkt_cont_opacity { - double nu; // frequency at which opacity was calculated - double total; - double es; - double ff; - double bf; - double ffheating; +struct rpkt_continuum_absorptioncoeffs { + double nu = NAN; // frequency at which opacity was calculated + double total = 0.; + double es = 0.; + double ff = 0.; + double bf = 0.; + double ffheating = 0.; // double bfheating; - int modelgridindex; - bool recalculate_required; // e.g. when cell or timestep has changed + int modelgridindex = -1; + bool recalculate_required = true; // e.g. when cell or timestep has changed }; template @@ -177,7 +174,7 @@ using chphixstargets_t = struct _chphixstargets; #include "macroatom.h" struct chlevels { - double processrates[MA_ACTION_COUNT]; + std::array processrates; chphixstargets_t *chphixstargets; double bfheatingcoeff; double population; @@ -207,7 +204,7 @@ namespace globals { extern double syn_dir[3]; // vector pointing from origin to observer -extern struct time *time_step; +extern std::unique_ptr timesteps; extern double *rpkt_emiss; @@ -235,7 +232,7 @@ extern bool do_emission_res; extern std::unique_ptr startofline; -extern double gamma_grey; +extern double gamma_kappagrey; constexpr double GREY_OP = 0.1; @@ -244,21 +241,15 @@ extern double max_path_step; extern int opacity_case; extern int nlines; -extern struct elementlist_entry *elements; +extern std::vector elements; + extern const struct linelist_entry *linelist; extern struct bflist_t *bflist; -extern double *spontrecombcoeff; - -// for USE_LUT_PHOTOION = true -extern double *corrphotoioncoeff; - // for USE_LUT_BFHEATING = true extern double *bfheating_coeff; -extern double *bfcooling_coeff; - -extern struct rpkt_cont_opacity *kappa_rpkt_cont; +extern struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont; extern int ncoolingterms; @@ -291,18 +282,17 @@ extern int node_count; extern int node_id; extern const int npkts; -extern int nesc; +extern std::atomic nesc; -extern double coordmax[3]; extern double vmax; extern double rmax; extern double tmax; extern double tmin; -extern int ntstep; -extern int itstep; -extern int ftstep; -extern int nts_global; +extern int ntimesteps; +extern int timestep_initial; +extern int timestep_finish; +extern int timestep; extern double opcase3_normal; extern double rho_crit_para; @@ -310,18 +300,18 @@ extern double rho_crit; extern int total_nlte_levels; -extern bool homogeneous_abundances; - extern bool simulation_continued_from_saved; extern double nu_rfcut; extern int num_lte_timesteps; extern double cell_is_optically_thick; extern int num_grey_timesteps; extern int n_titer; -extern bool initial_iteration; +extern bool lte_iteration; extern int n_kpktdiffusion_timesteps; extern float kpktdiffusion_timescale; +void setup_mpi_vars(); + } // namespace globals #endif // GLOBALS_H \ No newline at end of file diff --git a/grid.cc b/grid.cc index 73f8443eb..3e6037da6 100644 --- a/grid.cc +++ b/grid.cc @@ -8,18 +8,24 @@ #include #include #include +#include +#include #include #include #include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "input.h" #include "nltepop.h" #include "nonthermal.h" +#include "packet.h" #include "radfield.h" -#include "rpkt.h" #include "sn3d.h" +#include "stats.h" #include "vectors.h" namespace grid { @@ -30,15 +36,13 @@ int ncoordgrid[3]; /// propagation grid dimensions int ngrid; char coordlabel[3]; -enum model_types model_type = RHO_1D_READ; +enum gridtypes model_type = GRID_SPHERICAL1D; int npts_model = 0; // number of model grid cells int nonempty_npts_model = 0; // number of allocated non-empty model grid cells double t_model = -1.; // time at which densities in input model are correct. double *vout_model = nullptr; int ncoord_model[3]; // the model.txt input grid dimensions -double dcoord1; -double dcoord2; // spacings of a 2D model grid - must be uniform grid double min_den; // minimum model density @@ -49,9 +53,9 @@ int first_cellindex = -1; // auto-dermine first cell index in model.txt (usuall struct gridcell *cell = nullptr; -static int *mg_associated_cells = nullptr; -static int *nonemptymgi_of_mgi = nullptr; -static int *mgi_of_nonemptymgi = nullptr; +static std::vector mg_associated_cells; +static std::vector nonemptymgi_of_mgi; +static std::vector mgi_of_nonemptymgi; double *totmassradionuclide = nullptr; /// total mass of each radionuclide in the ejecta @@ -67,20 +71,17 @@ std::vector ranks_ndo; std::vector ranks_ndo_nonempty; int maxndo = -1; -auto get_mtot_input() -> double -// mass of the input model, which can be slightly different to the simulation mass -// e.g. spherical shells mapped to cartesian grid -{ - return mtot_input; -} - -auto wid_init(const int cellindex) -> double +auto wid_init(const int cellindex, const int axis) -> double // for a uniform grid this is the extent along the x,y,z coordinate (x_2 - x_1, etc.) // for spherical grid this is the radial extent (r_outer - r_inner) -// these values are for time globals::tmin +// these values are at time globals::tmin { - if constexpr (GRID_TYPE == GRID_UNIFORM) { - return 2 * globals::coordmax[0] / ncoordgrid[0]; + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + return 2 * globals::rmax / ncoordgrid[axis]; + } + + if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + return (axis == 0) ? globals::rmax / ncoordgrid[axis] : 2 * globals::rmax / ncoordgrid[axis]; } if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { @@ -96,14 +97,18 @@ auto get_modelcell_assocvolume_tmin(const int modelgridindex) -> double // return the model cell volume (when mapped to the propagation cells) at globals::tmin // for a uniform cubic grid this is constant { - if constexpr (GRID_TYPE == GRID_UNIFORM) { - return (wid_init(0) * wid_init(0) * wid_init(0)) * get_numassociatedcells(modelgridindex); + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + return (wid_init(modelgridindex, 0) * wid_init(modelgridindex, 1) * wid_init(modelgridindex, 2)) * + get_numassociatedcells(modelgridindex); + } + + if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + return wid_init(modelgridindex, 1) * PI * + (pow(get_cellcoordmax(modelgridindex, 0), 2) - pow(get_cellcoordmin(modelgridindex, 0), 2)); } if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { - return 4. / 3. * PI * - (pow(globals::tmin * vout_model[modelgridindex], 3) - - pow(globals::tmin * (modelgridindex > 0 ? vout_model[modelgridindex - 1] : 0.), 3)); + return 4. / 3. * PI * (pow(get_cellcoordmax(modelgridindex, 0), 3) - pow(get_cellcoordmin(modelgridindex, 0), 3)); } assert_always(false); @@ -113,28 +118,31 @@ auto get_gridcell_volume_tmin(const int cellindex) -> double // return the propagation cell volume at globals::tmin // for a spherical grid, the cell index is required (and should be equivalent to a modelgridindex) { - if constexpr (GRID_TYPE == GRID_UNIFORM) { - return (wid_init(0) * wid_init(0) * wid_init(0)); - } - - if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { - const int mgi = get_cell_modelgridindex(cellindex); - return get_modelcell_assocvolume_tmin(mgi); + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + return (wid_init(cellindex, 0) * wid_init(cellindex, 0) * wid_init(cellindex, 0)); } - assert_always(false); + // 2D and 1D with direct mapping to propagation cells + const int mgi = get_cell_modelgridindex(cellindex); + return get_modelcell_assocvolume_tmin(mgi); } auto get_cellcoordmax(const int cellindex, const int axis) -> double // get the minimum value of a coordinate at globals::tmin (xyz or radial coords) of a propagation cell // e.g., the minimum x position in xyz coords, or the minimum radius { - if constexpr (GRID_TYPE == GRID_UNIFORM) { - return grid::get_cellcoordmin(cellindex, axis) + grid::wid_init(0); + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + return grid::get_cellcoordmin(cellindex, axis) + grid::wid_init(0, axis); + } + + if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + assert_testmodeonly(axis <= 1); + return grid::get_cellcoordmin(cellindex, axis) + grid::wid_init(cellindex, axis); } if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { - return grid::get_cellcoordmin(cellindex, 0) + grid::wid_init(cellindex); + assert_testmodeonly(axis == 0); + return grid::get_cellcoordmin(cellindex, axis) + grid::wid_init(cellindex, axis); } assert_always(false); @@ -148,40 +156,54 @@ auto get_cellcoordmin(const int cellindex, const int axis) -> double // return - coordmax[axis] + (2 * get_cellcoordpointnum(cellindex, axis) * coordmax[axis] / ncoordgrid[axis]); } +static auto get_cell_r_inner(const int cellindex) -> double { + if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + return get_cellcoordmin(cellindex, 0); + } + + if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + const auto rcyl_inner = get_cellcoordmin(cellindex, 0); + const auto z_inner = std::min(std::abs(get_cellcoordmin(cellindex, 1)), std::abs(get_cellcoordmax(cellindex, 1))); + return std::sqrt(std::pow(rcyl_inner, 2) + std::pow(z_inner, 2)); + } + + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + const auto x_inner = std::min(std::abs(get_cellcoordmin(cellindex, 0)), std::abs(get_cellcoordmax(cellindex, 0))); + const auto y_inner = std::min(std::abs(get_cellcoordmin(cellindex, 1)), std::abs(get_cellcoordmax(cellindex, 1))); + const auto z_inner = std::min(std::abs(get_cellcoordmin(cellindex, 2)), std::abs(get_cellcoordmax(cellindex, 2))); + return std::sqrt(std::pow(x_inner, 2) + std::pow(y_inner, 2) + std::pow(z_inner, 2)); + } +} + auto get_coordcellindexincrement(const int axis) -> int // how much do we change the cellindex to move along a coordinately axis (e.g., the x, y, z directions, or r direction) { - if constexpr (GRID_TYPE == GRID_UNIFORM) { - switch (axis) { - case 0: - return 1; + // assert_testmodeonly(axis < get_ngriddimensions()); - case 1: - return ncoordgrid[0]; + switch (axis) { + case 0: + return 1; - case 2: - return ncoordgrid[0] * ncoordgrid[1]; + case 1: + return ncoordgrid[0]; - default: - printout("invalid coordinate index %d", axis); - abort(); - return -1; - } - } + case 2: + return ncoordgrid[0] * ncoordgrid[1]; - if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { - return 1; + default: + printout("invalid coordinate index %d", axis); + abort(); + return -1; } - - assert_always(false); } auto get_cellcoordpointnum(const int cellindex, const int axis) -> int // convert a cell index number into an integer (x,y,z or r) coordinate index from 0 to ncoordgrid[axis] { - if constexpr (GRID_TYPE == GRID_UNIFORM) { + if constexpr (GRID_TYPE == GRID_CARTESIAN3D || GRID_TYPE == GRID_CYLINDRICAL2D) { switch (axis) { - // increment x first, then y, then z + // 3D Cartesian: increment x first, then y, then z + // 2D Cylindrical: increment r first, then z case 0: return cellindex % ncoordgrid[0]; @@ -274,7 +296,7 @@ auto get_W(int modelgridindex) -> float { static void set_rho_tmin(int modelgridindex, float x) { modelgrid[modelgridindex].rhoinit = x; } -static void set_rho(int modelgridindex, float x) { modelgrid[modelgridindex].rho = x; } +void set_rho(int modelgridindex, float x) { modelgrid[modelgridindex].rho = x; } void set_nne(int modelgridindex, float nne) { modelgrid[modelgridindex].nne = nne; } @@ -300,9 +322,9 @@ void set_TJ(int modelgridindex, float TJ) { modelgrid[modelgridindex].TJ = TJ; } void set_W(int modelgridindex, float W) { modelgrid[modelgridindex].W = W; } -auto get_model_type() -> enum model_types { return model_type; } +auto get_model_type() -> enum gridtypes { return model_type; } -void set_model_type(enum model_types model_type_value) { model_type = model_type_value; } +void set_model_type(enum gridtypes model_type_value) { model_type = model_type_value; } auto get_npts_model() -> int // number of model grid cells @@ -324,10 +346,8 @@ static void set_npts_model(int new_npts_model) { assert_always(modelgrid == nullptr); modelgrid = static_cast(calloc(npts_model + 1, sizeof(struct modelgrid_t))); assert_always(modelgrid != nullptr); - assert_always(mg_associated_cells == nullptr); - mg_associated_cells = static_cast(malloc((npts_model + 1) * sizeof(int))); - assert_always(nonemptymgi_of_mgi == nullptr); - nonemptymgi_of_mgi = static_cast(malloc((npts_model + 1) * sizeof(int))); + mg_associated_cells.resize(npts_model + 1); + nonemptymgi_of_mgi.resize(npts_model + 1); } static void allocate_initradiobund() { @@ -400,7 +420,6 @@ static void set_cell_modelgridindex(int cellindex, int new_modelgridindex) { auto get_numassociatedcells(const int modelgridindex) -> int // number of propagation cells associated with each modelgrid cell { - assert_testmodeonly(mg_associated_cells != nullptr); assert_testmodeonly(modelgridindex <= get_npts_model()); return mg_associated_cells[modelgridindex]; } @@ -560,18 +579,28 @@ static void set_elem_stable_abund_from_total(const int mgi, const int element, c modelgrid[mgi].composition[element].abundance = isofracsum + massfracstable; } -auto get_cellradialpos(const int cellindex) -> double -// get the radial distance from the origin to the centre of the cell +static auto get_cellradialposmid(const int cellindex) -> double +// get the radial distance from the origin to the centre of the cell at time tmin { - // spherical coordinate case is trivial if (GRID_TYPE == GRID_SPHERICAL1D) { - return get_cellcoordmin(cellindex, 0) + (0.5 * wid_init(cellindex)); + // mid point radius + // return get_cellcoordmin(cellindex, 0) + (0.5 * wid_init(cellindex, 0)); + // volume averaged mean radius is slightly complex for radial shells + const double r_inner = grid::get_cellcoordmin(cellindex, 0); + const double r_outer = r_inner + grid::wid_init(cellindex, 0); + return 3. / 4 * (pow(r_outer, 4.) - pow(r_inner, 4.)) / (pow(r_outer, 3) - pow(r_inner, 3.)); + } + + if (GRID_TYPE == GRID_CYLINDRICAL2D) { + const double rcyl_mid = get_cellcoordmin(cellindex, 0) + (0.5 * wid_init(cellindex, 0)); + const double z_mid = get_cellcoordmin(cellindex, 1) + (0.5 * wid_init(cellindex, 1)); + return std::sqrt(std::pow(rcyl_mid, 2) + std::pow(z_mid, 2)); } // cubic grid requires taking the length of the 3D position vector double dcen[3]; for (int axis = 0; axis < 3; axis++) { - dcen[axis] = get_cellcoordmin(cellindex, axis) + (0.5 * wid_init(0)); + dcen[axis] = get_cellcoordmin(cellindex, axis) + (0.5 * wid_init(cellindex, axis)); } return vec_len(dcen); @@ -589,7 +618,7 @@ static void calculate_kappagrey() { double rho_sum = 0.0; double fe_sum = 0.0; double opcase3_sum = 0.0; - int const empty_cells = 0; + const int empty_cells = 0; for (int n = 0; n < ngrid; n++) { const int mgi = get_cell_modelgridindex(n); @@ -620,7 +649,7 @@ static void calculate_kappagrey() { grid_file = fopen_required("grid.out", "w"); } - /// Second pass through allows calculation of normalized kappa_grey + /// Second pass through allows calculation of normalized chi_grey double check1 = 0.0; double check2 = 0.0; for (int n = 0; n < ngrid; n++) { @@ -820,13 +849,26 @@ static void allocate_nonemptymodelcells() { // Determine the number of simulation cells associated with the model cells for (int mgi = 0; mgi < (get_npts_model() + 1); mgi++) { mg_associated_cells[mgi] = 0; + modelgrid[mgi].initial_radial_pos_sum = 0.; } for (int cellindex = 0; cellindex < ngrid; cellindex++) { + const auto radial_pos_mid = get_cellradialposmid(cellindex); + + if (FORCE_SPHERICAL_ESCAPE_SURFACE && radial_pos_mid > globals::vmax * globals::tmin) { + // for 1D models, the final shell outer v should already be at vmax + assert_always(model_type != GRID_SPHERICAL1D || cell[cellindex].modelgridindex == get_npts_model()); + cell[cellindex].modelgridindex = get_npts_model(); + } + const int mgi = get_cell_modelgridindex(cellindex); - assert_always(!(get_model_type() == RHO_3D_READ) || (get_rho_tmin(mgi) > 0) || (mgi == get_npts_model())); + assert_always(!(get_model_type() == GRID_CARTESIAN3D) || (get_rho_tmin(mgi) > 0) || (mgi == get_npts_model())); + mg_associated_cells[mgi] += 1; - assert_always(!(get_model_type() == RHO_3D_READ) || (mg_associated_cells[mgi] == 1) || (mgi == get_npts_model())); + modelgrid[mgi].initial_radial_pos_sum += radial_pos_mid; + + assert_always(!(get_model_type() == GRID_CARTESIAN3D) || (mg_associated_cells[mgi] == 1) || + (mgi == get_npts_model())); } // find number of non-empty cells and allocate nonempty list @@ -838,8 +880,7 @@ static void allocate_nonemptymodelcells() { } assert_always(nonempty_npts_model > 0); - assert_always(mgi_of_nonemptymgi == nullptr); - mgi_of_nonemptymgi = static_cast(malloc((nonempty_npts_model) * sizeof(int))); + mgi_of_nonemptymgi.resize(nonempty_npts_model); int nonemptymgi = 0; // index within list of non-empty modelgrid cells @@ -881,96 +922,61 @@ static void allocate_nonemptymodelcells() { get_nonempty_npts_model() * globals::total_nlte_levels * sizeof(double) / 1024. / 1024.); } -static void map_1dmodeltogrid() +static void map_1dmodelto3dgrid() // Map 1D spherical model grid onto propagation grid { for (int cellindex = 0; cellindex < ngrid; cellindex++) { - const double radial_pos = get_cellradialpos(cellindex); - const double vcell = radial_pos / globals::tmin; - const double vmin = 0.; - if (radial_pos < globals::rmax) { - if (GRID_TYPE == GRID_SPHERICAL1D) { - set_cell_modelgridindex(cellindex, cellindex); - } else { - int mgi = 0; + const double cellvmid = get_cellradialposmid(cellindex) / globals::tmin; + const int mgi = + std::distance(vout_model, std::find_if_not(vout_model, vout_model + get_npts_model(), + [cellvmid](double v_outer) { return v_outer < cellvmid; })); - for (int i = 0; i < (get_npts_model() - 1); i++) { - if (vout_model[mgi] < vcell) { - mgi = i + 1; - } - } - set_cell_modelgridindex(cellindex, mgi); - } - const int mgi = get_cell_modelgridindex(cellindex); - if ((vout_model[mgi] >= vmin) && (get_rho_tmin(mgi) > 0)) { - modelgrid[mgi].initial_radial_pos_sum += radial_pos; - } else { - set_cell_modelgridindex(cellindex, get_npts_model()); - } + if (mgi < get_npts_model() && modelgrid[mgi].rhoinit > 0) { + set_cell_modelgridindex(cellindex, mgi); } else { set_cell_modelgridindex(cellindex, get_npts_model()); } } } -static void map_2dmodeltogrid() +static void map_2dmodelto3dgrid() // Map 2D cylindrical model onto propagation grid { - for (int n = 0; n < ngrid; n++) { - const double radial_pos = get_cellradialpos(n); - - if (radial_pos < globals::rmax) { - double dcen[3]; - for (int d = 0; d < 3; d++) { - const double cellcoordmin = - -globals::coordmax[d] + (2 * get_cellcoordpointnum(n, d) * globals::coordmax[d] / ncoordgrid[0]); - dcen[d] = cellcoordmin + (0.5 * wid_init(0)); - } + for (int cellindex = 0; cellindex < ngrid; cellindex++) { + int mgi = get_npts_model(); // default to empty unless set - set_cell_modelgridindex(n, 0); - const double zcylindrical = dcen[2]; - dcen[2] = 0.0; - const double rcylindrical = vec_len(dcen); + // map to 3D Cartesian grid + double pos_mid[3]; + for (int d = 0; d < 3; d++) { + pos_mid[d] = (get_cellcoordmin(cellindex, d) + (0.5 * wid_init(cellindex, d))); + } - // Grid is uniform so only need to search in 1d to get r and z positions + // 2D grid is uniform so rcyl and z positions can easily be calculated + const double rcylindrical = std::sqrt(std::pow(pos_mid[0], 2) + std::pow(pos_mid[1], 2)); - int mkeep1 = 0; - for (int m = 0; m < ncoord_model[0]; m++) { - if (rcylindrical > (m * dcoord1 * globals::tmin / t_model)) { - mkeep1 = m; - // set_cell_modelgridindex(n, m + 1); - } - } + const int n_rcyl = static_cast(rcylindrical / globals::tmin / globals::vmax * ncoord_model[0]); + const int n_z = + static_cast((pos_mid[2] / globals::tmin + globals::vmax) / (2 * globals::vmax) * ncoord_model[1]); - int mkeep2 = 0; - for (int m = 0; m < ncoord_model[1]; m++) { - if (zcylindrical > (((m * dcoord2) * globals::tmin / t_model) - globals::rmax)) { - mkeep2 = m; - // set_cell_modelgridindex(n, m + 1); - } - } - set_cell_modelgridindex(n, (mkeep2 * ncoord_model[0]) + mkeep1); - modelgrid[get_cell_modelgridindex(n)].initial_radial_pos_sum += radial_pos; + if (n_rcyl >= 0 && n_rcyl < ncoord_model[0] && n_z >= 0 && n_z < ncoord_model[1]) { + mgi = (n_z * ncoord_model[0]) + n_rcyl; + } - // renorm[mkeep]++; + if (modelgrid[mgi].rhoinit > 0) { + set_cell_modelgridindex(cellindex, mgi); } else { - set_cell_modelgridindex(n, get_npts_model()); + set_cell_modelgridindex(cellindex, get_npts_model()); } } } -static void map_3dmodeltogrid() { - // propagation grid must match the input model grid exactly for 3D models - assert_always(ncoord_model[0] == ncoordgrid[0]); - assert_always(ncoord_model[1] == ncoordgrid[1]); - assert_always(ncoord_model[2] == ncoordgrid[2]); - +static void map_modeltogrid_direct() +// mgi and cellindex are interchangeable in this mode +{ for (int cellindex = 0; cellindex < ngrid; cellindex++) { - // mgi and cellindex are interchangeable in this mode - const int mgi = cellindex; - modelgrid[mgi].initial_radial_pos_sum = get_cellradialpos(cellindex); - const bool keepcell = (get_rho_tmin(mgi) > 0); - if (keepcell) { + const int mgi = cellindex; // direct mapping + + if (modelgrid[mgi].rhoinit > 0) { set_cell_modelgridindex(cellindex, mgi); } else { set_cell_modelgridindex(cellindex, get_npts_model()); @@ -984,10 +990,10 @@ static void abundances_read() { MPI_Barrier(MPI_COMM_WORLD); #endif printout("reading abundances.txt..."); - const bool threedimensional = (get_model_type() == RHO_3D_READ); + const bool threedimensional = (get_model_type() == GRID_CARTESIAN3D); /// Open the abundances file - std::ifstream abundance_file("abundances.txt"); + auto abundance_file = fstream_required("abundances.txt", std::ios::in); /// and process through the grid to read in the abundances per cell /// The abundance file should only contain information for non-empty @@ -996,20 +1002,19 @@ static void abundances_read() { /// i.e. in total one integer and 30 floats. // loop over propagation cells for 3D models, or modelgrid cells - const int npts_model = get_npts_model(); - for (int mgi = 0; mgi < npts_model; mgi++) { + for (int mgi = 0; mgi < get_npts_model(); mgi++) { std::string line; assert_always(get_noncommentline(abundance_file, line)); std::istringstream ssline(line); int cellnumberinput = -1; assert_always(ssline >> cellnumberinput); - assert_always(cellnumberinput == mgi + first_cellindex) + assert_always(cellnumberinput == mgi + first_cellindex); - // the abundances.txt file specifies the elemental mass fractions for each model cell - // (or proportial to mass frac, e.g. element densities because they will be normalised anyway) - // The abundances begin with hydrogen, helium, etc, going as far up the atomic numbers as required - double normfactor = 0.; + // the abundances.txt file specifies the elemental mass fractions for each model cell + // (or proportial to mass frac, e.g. element densities because they will be normalised anyway) + // The abundances begin with hydrogen, helium, etc, going as far up the atomic numbers as required + double normfactor = 0.; float abundances_in[150] = {0.}; for (int anumber = 1; anumber <= 150; anumber++) { abundances_in[anumber - 1] = 0.; @@ -1040,7 +1045,6 @@ static void abundances_read() { } } - abundance_file.close(); #ifdef MPI_ON // barrier to make sure node master has set values in node shared memory MPI_Barrier(MPI_COMM_WORLD); @@ -1048,13 +1052,8 @@ static void abundances_read() { printout("done.\n"); } -static auto str_starts_with(const std::string &str, const std::string &strprefix) -> bool { - // return true if str starts with strprefix - return (str.rfind(strprefix, 0) == 0); -} - -static void read_model_headerline(const std::string &line, std::vector &zlist, std::vector &alist, - std::vector &columnname) { +static void parse_model_headerline(const std::string &line, std::vector &zlist, std::vector &alist, + std::vector &colnames) { // custom header line std::istringstream iss(line); std::string token; @@ -1062,7 +1061,7 @@ static void read_model_headerline(const std::string &line, std::vector &zli int columnindex = -1; while (std::getline(iss, token, ' ')) { - if (std::all_of(token.begin(), token.end(), isspace)) { // skip whitespace tokens + if (std::ranges::all_of(token, isspace)) { // skip whitespace tokens continue; } @@ -1072,172 +1071,181 @@ static void read_model_headerline(const std::string &line, std::vector &zli assert_always(columnindex == 0); } else if (token == "velocity_outer") { assert_always(columnindex == 1); + } else if (token == "vel_r_max_kmps") { + assert_always(columnindex == 1); + } else if (token.starts_with("pos_")) { + continue; } else if (token == "logrho") { // 1D models have log10(rho [g/cm3]) assert_always(columnindex == 2); - assert_always(get_model_type() == RHO_1D_READ); + assert_always(get_model_type() == GRID_SPHERICAL1D); } else if (token == "rho") { - // 2D amd 3D models have rho [g/cm3] - assert_always(columnindex == 4); - assert_always(get_model_type() != RHO_1D_READ); + // 2D and 3D models have rho [g/cm3] + assert_always(get_model_type() != GRID_SPHERICAL1D); + assert_always((columnindex == 4 && get_model_type() == GRID_CARTESIAN3D) || + (columnindex == 3 && get_model_type() == GRID_CYLINDRICAL2D)); continue; } else if (token == "X_Fegroup") { - continue; - } else if (token == "X_Ni56") { - continue; - } else if (token == "X_Co56") { - continue; - } else if (token == "X_Fe52") { - continue; - } else if (token == "X_Cr48") { - continue; - } else if (token == "X_Ni57") { - continue; - } else if (token == "X_Co57") { - continue; - } else if (str_starts_with(token, "pos_")) { - continue; + colnames.push_back(token); + zlist.push_back(-1); + alist.push_back(-1); + } else if (token.starts_with("X_")) { + colnames.push_back(token); + const int z = decay::get_nucstring_z(token.substr(2)); // + 2 skips the 'X_' + const int a = decay::get_nucstring_a(token.substr(2)); + assert_always(z >= 0); + assert_always(a >= 0); + // printout("Custom column: '%s' Z %d A %d\n", token.c_str(), z, a); + zlist.push_back(z); + alist.push_back(a); } else { - assert_always(get_model_type() != RHO_1D_READ || columnindex >= 10); - assert_always(get_model_type() != RHO_3D_READ || columnindex >= 12); - - columnname.push_back(token); - - if (str_starts_with(token, "X_")) { - const int z = decay::get_nucstring_z(token.substr(2)); // + 2 skips the 'X_' - const int a = decay::get_nucstring_a(token.substr(2)); - assert_always(z >= 0); - assert_always(a >= 0); - // printout("Custom column: '%s' Z %d A %d\n", token.c_str(), z, a); - zlist.push_back(z); - alist.push_back(a); - } else { - // printout("Custom column: '%s' Z %d A %d\n", token.c_str(), -1, -1); - zlist.push_back(-1); - alist.push_back(-1); - } + // printout("Custom column: '%s' Z %d A %d\n", token.c_str(), -1, -1); + colnames.push_back(token); + zlist.push_back(-1); + alist.push_back(-1); } } +} - // alternative: - // while(iss >> token) - // { - // printout("Custom header column: %s\n", token.c_str()); - // columns.push_back(token); - // } - - // for(std::vector::iterator it = columns.begin(); it != columns.end(); ++it) - // { - // printout("Repeat of Custom header column: %s\n", it->c_str()); - // } +static auto get_token_count(std::string &line) -> int { + std::string token; + int abundcolcount = 0; + auto ssline = std::istringstream(line); + while (std::getline(ssline, token, ' ')) { + if (!std::ranges::all_of(token, isspace)) { // skip whitespace tokens + abundcolcount++; + } + } + return abundcolcount; } -static void read_model_radioabundances(std::ifstream &fmodel, std::string &line, const int linepos, const int mgi, - const bool keepcell, std::vector &colnames, - std::vector &nucindexlist) { - bool one_line_per_cell = false; +static void read_model_radioabundances(std::fstream &fmodel, std::istringstream &ssline_in, const int mgi, + const bool keepcell, const std::vector &colnames, + const std::vector &nucindexlist, const bool one_line_per_cell) { + std::string line; + if (!one_line_per_cell) { + assert_always(std::getline(fmodel, line)); + } - if (linepos < static_cast(line.length())) { - // still more line is remaining, so any non-whitespace chars mean that - // the abundances are on the same line - line = line.substr(linepos); - for (const char &c : line) { - if (isspace(c) == 0) { - one_line_per_cell = true; - break; + auto ssline = one_line_per_cell ? std::move(ssline_in) : std::istringstream(line); + + if (!keepcell) { + return; + } + + for (size_t i = 0; i < colnames.size(); i++) { + double valuein = 0.; + assert_always(ssline >> valuein); // usually a mass fraction, but now can be anything + + if (nucindexlist[i] >= 0) { + assert_testmodeonly(valuein >= 0.); + assert_testmodeonly(valuein <= 1.); + set_modelinitradioabund(mgi, nucindexlist[i], valuein); + } else if (colnames[i] == "X_Fegroup") { + set_ffegrp(mgi, valuein); + } else if (colnames[i] == "cellYe") { + set_initelectronfrac(mgi, valuein); + } else if (colnames[i] == "q") { + // use value for t_model and adjust to tmin with expansion factor + set_initenergyq(mgi, valuein * t_model / globals::tmin); + } else if (colnames[i] == "tracercount") { + ; + } else { + if (mgi == 0) { + printout("WARNING: ignoring column '%s' nucindex %d valuein[mgi=0] %lg\n", colnames[i].c_str(), nucindexlist[i], + valuein); } } } + double valuein = 0.; + assert_always(!(ssline >> valuein)); // should be no tokens left! +} - if (one_line_per_cell) { - if (mgi == 0) { - printout("model.txt has has single line per cell format\n"); - } +static auto read_model_columns(std::fstream &fmodel) -> std::tuple, std::vector, bool> { + auto pos_data_start = fmodel.tellg(); // get position in case we need to undo getline + + std::vector zlist; + std::vector alist; + std::vector colnames; + + std::string line; + std::getline(fmodel, line); + + std::string headerline; + + const bool header_specified = lineiscommentonly(line); + + if (header_specified) { + // line is the header + headerline = line; + pos_data_start = fmodel.tellg(); + std::getline(fmodel, line); } else { - // we reached the end of this line before abundances were read - if (mgi == 0) { - printout("model.txt has has two lines per cell format\n"); + // line is not a comment, so it must be the first line of data + // add a default header for unlabelled columns + switch (model_type) { + case GRID_SPHERICAL1D: + headerline = "#inputcellid vel_r_max_kmps logrho"; + break; + case GRID_CYLINDRICAL2D: + headerline = "#inputcellid pos_rcyl_mid pos_z_mid rho"; + break; + case GRID_CARTESIAN3D: + headerline = "#inputcellid pos_x_min pos_y_min pos_z_min rho"; + break; } - assert_always(std::getline(fmodel, line)); + headerline += " X_Fegroup X_Ni56 X_Co56 X_Fe52 X_Cr48"; } - std::istringstream ssline(line); - double f56ni_model = 0.; - double f56co_model = 0.; - double ffegrp_model = 0.; - double f48cr_model = 0.; - double f52fe_model = 0.; - double f57ni_model = 0.; - double f57co_model = 0.; - const int items_read = sscanf(line.c_str(), "%lg %lg %lg %lg %lg %lg %lg", &ffegrp_model, &f56ni_model, &f56co_model, - &f52fe_model, &f48cr_model, &f57ni_model, &f57co_model); + int colcount = get_token_count(line); + const bool one_line_per_cell = (colcount >= get_token_count(headerline)); - if (items_read == 5 || items_read == 7) { - if (items_read == 7 && mgi == 0) { - printout("Found Ni57 and Co57 abundance columns in model.txt\n"); - } + printout("model.txt has %s line per cell format\n", one_line_per_cell ? "one" : "two"); - // printout("mgi %d ni56 %g co56 %g fe52 %g cr48 %g ni57 %g co57 %g\n", - // mgi, f56ni_model, f56co_model, f52fe_model, f48cr_model, f57ni_model, f57co_model); + if (!one_line_per_cell) { // add columns from the second line + std::getline(fmodel, line); + colcount += get_token_count(line); + } - if (keepcell) { - set_modelinitradioabund(mgi, decay::get_nucindex(28, 56), f56ni_model); - set_modelinitradioabund(mgi, decay::get_nucindex(27, 56), f56co_model); - set_modelinitradioabund(mgi, decay::get_nucindex(26, 52), f52fe_model); - set_modelinitradioabund(mgi, decay::get_nucindex(24, 48), f48cr_model); - set_modelinitradioabund(mgi, decay::get_nucindex(23, 48), 0.); - set_modelinitradioabund(mgi, decay::get_nucindex(28, 57), f57ni_model); - set_modelinitradioabund(mgi, decay::get_nucindex(27, 57), f57co_model); - - set_ffegrp(mgi, ffegrp_model); - - if (items_read == 7) { - for (int i = 0; i < items_read; i++) { - double abundin = 0.; - assert_always(ssline >> abundin); // ignore - } + if (!header_specified && colcount > get_token_count(headerline)) { + headerline += " X_Ni57 X_Co57"; + } - for (int i = 0; i < static_cast(colnames.size()); i++) { - double valuein = 0.; - assert_always(ssline >> valuein); // usually a mass fraction, but now can be anything - if (nucindexlist[i] >= 0) { - set_modelinitradioabund(mgi, nucindexlist[i], valuein); - } else if (colnames[i] == "cellYe") { - set_initelectronfrac(mgi, valuein); - } else if (colnames[i] == "q") { - // use value for t_model and adjust to tmin with expansion factor - set_initenergyq(mgi, valuein * t_model / globals::tmin); - } else if (colnames[i] == "tracercount") { - ; - } else { - printout("Not sure what to do with column %s nucindex %d valuein %lg\n", colnames[i].c_str(), - nucindexlist[i], valuein); - assert_always(false); - } - } - double valuein = 0.; - assert_always(!(ssline >> valuein)); // should be no tokens left! - } - } + assert_always(colcount == get_token_count(headerline)); + + fmodel.seekg(pos_data_start); // get back to start of data + + if (header_specified) { + printout("model.txt has header line: %s\n", headerline.c_str()); } else { - printout("Unexpected number of values in model.txt. items_read = %d\n", items_read); - printout("line: %s\n", line.c_str()); - abort(); + printout("model.txt has no header line. Using default: %s\n", headerline.c_str()); + } + + parse_model_headerline(headerline, zlist, alist, colnames); + + decay::init_nuclides(zlist, alist); + + std::vector nucindexlist(zlist.size()); + for (std::size_t i = 0; i < zlist.size(); i++) { + nucindexlist[i] = (zlist[i] > 0) ? decay::get_nucindex(zlist[i], alist[i]) : -1; } + + allocate_initradiobund(); + + return std::make_tuple(colnames, nucindexlist, one_line_per_cell); } static void read_1d_model() // Read in a 1D spherical model { - std::ifstream fmodel("model.txt"); - assert_always(fmodel.is_open()); + auto fmodel = fstream_required("model.txt", std::ios::in); std::string line; // 1st read the number of data points in the table of input model. int npts_model_in = 0; assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> npts_model_in; + std::istringstream(line) >> npts_model_in; set_npts_model(npts_model_in); ncoord_model[0] = npts_model_in; @@ -1247,7 +1255,7 @@ static void read_1d_model() // Now read the time (in days) at which the model is specified. double t_model_days = NAN; assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> t_model_days; + std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; // Now read in the lines of the model. Each line has 5 entries: the @@ -1257,38 +1265,19 @@ static void read_1d_model() // in the cell (float). For now, the last number is recorded but never // used. - std::vector zlist; - std::vector alist; - std::vector colnames; - std::streampos const oldpos = fmodel.tellg(); // get position in case we need to undo getline - std::getline(fmodel, line); - if (lineiscommentonly(line)) { - read_model_headerline(line, zlist, alist, colnames); - } else { - fmodel.seekg(oldpos); // undo getline because it was data, not a header line - } - - decay::init_nuclides(zlist, alist); - allocate_initradiobund(); - - std::vector nucindexlist(zlist.size()); - for (int i = 0; i < static_cast(zlist.size()); i++) { - nucindexlist[i] = (zlist[i] > 0) ? decay::get_nucindex(zlist[i], alist[i]) : -1; - } + const auto [colnames, nucindexlist, one_line_per_cell] = read_model_columns(fmodel); int mgi = 0; while (std::getline(fmodel, line)) { - std::istringstream const ssline(line); double vout_kmps = NAN; double log_rho = NAN; int cellnumberin = 0; - int linepos = 0; - - const int items_read = sscanf(line.c_str(), "%d %lg %lg%n", &cellnumberin, &vout_kmps, &log_rho, &linepos); + std::istringstream ssline(line); - if (items_read == 3) { + if (ssline >> cellnumberin >> vout_kmps >> log_rho) { if (mgi == 0) { first_cellindex = cellnumberin; + printout("first_cellindex %d\n", first_cellindex); } assert_always(cellnumberin == mgi + first_cellindex); @@ -1298,12 +1287,11 @@ static void read_1d_model() set_rho_tmin(mgi, rho_tmin); set_rho(mgi, rho_tmin); } else { - printout("Unexpected number of values in model.txt. items_read = %d\n", items_read); + printout("Unexpected number of values in model.txt\n"); printout("line: %s\n", line.c_str()); assert_always(false); } - - read_model_radioabundances(fmodel, line, linepos, mgi, true, colnames, nucindexlist); + read_model_radioabundances(fmodel, ssline, mgi, true, colnames, nucindexlist, one_line_per_cell); mgi += 1; if (mgi == get_npts_model()) { @@ -1312,60 +1300,38 @@ static void read_1d_model() } if (mgi != get_npts_model()) { - printout("ERROR in model.txt. Found %d only cells instead of %d expected.\n", mgi - 1, get_npts_model()); + printout("ERROR in model.txt. Found only %d cells instead of %d expected.\n", mgi - 1, get_npts_model()); abort(); } - fmodel.close(); - globals::vmax = vout_model[get_npts_model() - 1]; } static void read_2d_model() // Read in a 2D axisymmetric spherical coordinate model { - std::ifstream fmodel("model.txt"); - assert_always(fmodel.is_open()); + auto fmodel = fstream_required("model.txt", std::ios::in); std::string line; // 1st read the number of data points in the table of input model. assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> ncoord_model[0] >> ncoord_model[1]; // r and z (cylindrical polar) + std::istringstream(line) >> ncoord_model[0] >> ncoord_model[1]; // r and z (cylindrical polar) + ncoord_model[2] = 0.; set_npts_model(ncoord_model[0] * ncoord_model[1]); // Now read the time (in days) at which the model is specified. double t_model_days = NAN; assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> t_model_days; + std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; /// Now read in vmax for the model (in cm s^-1). assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> globals::vmax; + std::istringstream(line) >> globals::vmax; - dcoord1 = globals::vmax * t_model / ncoord_model[0]; // dr for input model - dcoord2 = 2. * globals::vmax * t_model / ncoord_model[1]; // dz for input model - - std::vector zlist; - std::vector alist; - std::vector colnames; - std::streampos const oldpos = fmodel.tellg(); // get position in case we need to undo getline - std::getline(fmodel, line); - if (lineiscommentonly(line)) { - read_model_headerline(line, zlist, alist, colnames); - } else { - fmodel.seekg(oldpos); // undo getline because it was data, not a header line - } - - decay::init_nuclides(zlist, alist); - allocate_initradiobund(); - - std::vector nucindexlist(zlist.size()); - for (int i = 0; i < static_cast(zlist.size()); i++) { - nucindexlist[i] = (zlist[i] > 0) ? decay::get_nucindex(zlist[i], alist[i]) : -1; - } + const auto [colnames, nucindexlist, one_line_per_cell] = read_model_columns(fmodel); // Now read in the model. Each point in the model has two lines of input. // First is an index for the cell then its r-mid point then its z-mid point @@ -1373,33 +1339,42 @@ static void read_2d_model() // Second is the total FeG mass, initial 56Ni mass, initial 56Co mass int mgi = 0; + int nonemptymgi = 0; while (std::getline(fmodel, line)) { int cellnumberin = 0; float cell_r_in = NAN; float cell_z_in = NAN; double rho_tmodel = NAN; - int linepos = 0; - - assert_always( - sscanf(line.c_str(), "%d %g %g %lg%n", &cellnumberin, &cell_r_in, &cell_z_in, &rho_tmodel, &linepos) == 4); + std::istringstream ssline(line); + assert_always(ssline >> cellnumberin >> cell_r_in >> cell_z_in >> rho_tmodel); if (mgi == 0) { first_cellindex = cellnumberin; } assert_always(cellnumberin == mgi + first_cellindex); - const int ncoord1 = (mgi % ncoord_model[0]); - const double r_cylindrical = (ncoord1 + 0.5) * dcoord1; - assert_always(fabs(cell_r_in / r_cylindrical - 1) < 1e-3); - const int ncoord2 = (mgi / ncoord_model[0]); - const double z = -globals::vmax * t_model + ((ncoord2 + 0.5) * dcoord2); - assert_always(fabs(cell_z_in / z - 1) < 1e-3); + const int n_rcyl = (mgi % ncoord_model[0]); + const double pos_r_cyl_mid = (n_rcyl + 0.5) * globals::vmax * t_model / ncoord_model[0]; + assert_always(fabs(cell_r_in / pos_r_cyl_mid - 1) < 1e-3); + const int n_z = (mgi / ncoord_model[0]); + const double pos_z_mid = globals::vmax * t_model * (-1 + 2 * (n_z + 0.5) / ncoord_model[1]); + assert_always(fabs(cell_z_in / pos_z_mid - 1) < 1e-3); + + if (rho_tmodel < 0) { + printout("negative input density %g %d\n", rho_tmodel, mgi); + abort(); + } + const bool keepcell = (rho_tmodel > 0); const double rho_tmin = rho_tmodel * pow(t_model / globals::tmin, 3); set_rho_tmin(mgi, rho_tmin); set_rho(mgi, rho_tmin); - read_model_radioabundances(fmodel, line, linepos, mgi, true, colnames, nucindexlist); + read_model_radioabundances(fmodel, ssline, mgi, keepcell, colnames, nucindexlist, one_line_per_cell); + + if (keepcell) { + nonemptymgi++; + } mgi++; } @@ -1409,15 +1384,13 @@ static void read_2d_model() abort(); } - fmodel.close(); + printout("Effectively used model grid cells: %d\n", nonemptymgi); } static void read_3d_model() /// Subroutine to read in a 3-D model. { - printout("reading 3D model.txt...\n"); - std::ifstream fmodel("model.txt"); - assert_always(fmodel.is_open()); + auto fmodel = fstream_required("model.txt", std::ios::in); std::string line; @@ -1425,7 +1398,7 @@ static void read_3d_model() /// This MUST be the same number as the maximum number of points used in the grid - if not, abort. int npts_model_in = 0; assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> npts_model_in; + std::istringstream(line) >> npts_model_in; set_npts_model(npts_model_in); ncoord_model[0] = ncoord_model[1] = ncoord_model[2] = static_cast(round(pow(npts_model_in, 1 / 3.))); @@ -1439,14 +1412,14 @@ static void read_3d_model() double t_model_days = NAN; assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> t_model_days; + std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; /// Now read in vmax for the model (in cm s^-1). assert_always(get_noncommentline(fmodel, line)); - std::stringstream(line) >> globals::vmax; + std::istringstream(line) >> globals::vmax; - double const xmax_tmodel = globals::vmax * t_model; + const double xmax_tmodel = globals::vmax * t_model; /// Now read in the lines of the model. min_den = -1.; @@ -1456,24 +1429,7 @@ static void read_3d_model() bool posmatch_xyz = true; bool posmatch_zyx = true; - std::vector zlist; - std::vector alist; - std::vector colnames; - std::streampos const oldpos = fmodel.tellg(); // get position in case we need to undo getline - std::getline(fmodel, line); - if (lineiscommentonly(line)) { - read_model_headerline(line, zlist, alist, colnames); - } else { - fmodel.seekg(oldpos); // undo getline because it was data, not a header line - } - - decay::init_nuclides(zlist, alist); - allocate_initradiobund(); - - std::vector nucindexlist(zlist.size()); - for (int i = 0; i < static_cast(zlist.size()); i++) { - nucindexlist[i] = (zlist[i] > 0) ? decay::get_nucindex(zlist[i], alist[i]) : -1; - } + const auto [colnames, nucindexlist, one_line_per_cell] = read_model_columns(fmodel); // mgi is the index to the model grid - empty cells are sent to special value get_npts_model(), // otherwise each input cell is one modelgrid cell @@ -1483,10 +1439,9 @@ static void read_3d_model() int cellnumberin = 0; float cellpos_in[3]; float rho_model = NAN; - int linepos = 0; - int const items_read = sscanf(line.c_str(), "%d %g %g %g %g%n", &cellnumberin, &cellpos_in[0], &cellpos_in[1], - &cellpos_in[2], &rho_model, &linepos); - assert_always(items_read == 5); + std::istringstream ssline(line); + + assert_always(ssline >> cellnumberin >> cellpos_in[0] >> cellpos_in[1] >> cellpos_in[2] >> rho_model); // printout("cell %d, posz %g, posy %g, posx %g, rho %g, rho_init %g\n",dum1,dum3,dum4,dum5,rho_model,rho_model* // pow( (t_model/globals::tmin), 3.)); @@ -1530,7 +1485,7 @@ static void read_3d_model() min_den = rho_model; } - read_model_radioabundances(fmodel, line, linepos, mgi, keepcell, colnames, nucindexlist); + read_model_radioabundances(fmodel, ssline, mgi, keepcell, colnames, nucindexlist, one_line_per_cell); if (keepcell) { nonemptymgi++; @@ -1552,11 +1507,7 @@ static void read_3d_model() } printout("min_den %g [g/cm3]\n", min_den); - printout("Effectively used model grid cells %d\n", nonemptymgi); - - /// Now, set actual size of the modelgrid to the number of non-empty cells. - - fmodel.close(); + printout("Effectively used model grid cells: %d\n", nonemptymgi); } static void calc_modelinit_totmassradionuclides() { @@ -1571,31 +1522,29 @@ static void calc_modelinit_totmassradionuclides() { totmassradionuclide[nucindex] = 0.; } - int n1 = 0; + const double dcoord_rcyl = globals::vmax * t_model / ncoord_model[0]; // dr for input model + const double dcoord_z = 2. * globals::vmax * t_model / ncoord_model[1]; // dz for input model + for (int mgi = 0; mgi < get_npts_model(); mgi++) { if (get_rho_tmin(mgi) <= 0.) { continue; } double cellvolume = 0.; - if (get_model_type() == RHO_1D_READ) { + if (get_model_type() == GRID_SPHERICAL1D) { const double v_inner = (mgi == 0) ? 0. : vout_model[mgi - 1]; // mass_in_shell = rho_model[mgi] * (pow(vout_model[mgi], 3) - pow(v_inner, 3)) * 4 * PI * pow(t_model, 3) / 3.; cellvolume = (pow(vout_model[mgi], 3) - pow(v_inner, 3)) * 4 * PI * pow(globals::tmin, 3) / 3.; - } else if (get_model_type() == RHO_2D_READ) { - cellvolume = pow(globals::tmin / t_model, 3) * ((2 * n1) + 1) * PI * dcoord2 * pow(dcoord1, 2.); - n1++; - if (n1 == ncoord_model[0]) { - n1 = 0; - } - } else if (get_model_type() == RHO_3D_READ) { + } else if (get_model_type() == GRID_CYLINDRICAL2D) { + const int n_r = mgi % ncoord_model[0]; + cellvolume = pow(globals::tmin / t_model, 3) * dcoord_z * PI * + (pow((n_r + 1) * dcoord_rcyl, 2.) - pow(n_r * dcoord_rcyl, 2.)); + } else if (get_model_type() == GRID_CARTESIAN3D) { /// Assumes cells are cubes here - all same volume. cellvolume = pow((2 * globals::vmax * globals::tmin), 3.) / (ncoordgrid[0] * ncoordgrid[1] * ncoordgrid[2]); } else { printout("Unknown model type %d in function %s\n", get_model_type(), __func__); abort(); } - // can use grid::get_modelcell_assocvolume_tmin(mgi) to get actual simulated volume (with slight error versus - // input) const double mass_in_shell = get_rho_tmin(mgi) * cellvolume; @@ -1619,25 +1568,20 @@ static void calc_modelinit_totmassradionuclides() { void read_ejecta_model() { switch (get_model_type()) { - case RHO_UNIFORM: { - assert_always(false); // needs to be reimplemented using spherical coordinate mode - break; - } - - case RHO_1D_READ: { + case GRID_SPHERICAL1D: { printout("Read 1D model\n"); read_1d_model(); break; } - case RHO_2D_READ: { + case GRID_CYLINDRICAL2D: { printout("Read 2D model\n"); read_2d_model(); break; } - case RHO_3D_READ: { + case GRID_CARTESIAN3D: { printout("Read 3D model\n"); read_3d_model(); @@ -1657,8 +1601,6 @@ void read_ejecta_model() { printout("tmin %g [s] = %.2f [d]\n", globals::tmin, globals::tmin / 86400.); printout("rmax %g [cm] (at t=tmin)\n", globals::rmax); - globals::coordmax[0] = globals::coordmax[1] = globals::coordmax[2] = globals::rmax; - globals::rpkt_emiss = static_cast(calloc((get_npts_model() + 1), sizeof(double))); if constexpr (USE_LUT_PHOTOION) { @@ -1701,9 +1643,9 @@ static void read_grid_restart_data(const int timestep) { printout("READIN GRID SNAPSHOT from %s\n", filename); FILE *gridsave_file = fopen_required(filename, "r"); - int ntstep_in = -1; - assert_always(fscanf(gridsave_file, "%d ", &ntstep_in) == 1); - assert_always(ntstep_in == globals::ntstep); + int ntimesteps_in = -1; + assert_always(fscanf(gridsave_file, "%d ", &ntimesteps_in) == 1); + assert_always(ntimesteps_in == globals::ntimesteps); int nprocs_in = -1; assert_always(fscanf(gridsave_file, "%d ", &nprocs_in) == 1); @@ -1713,16 +1655,18 @@ static void read_grid_restart_data(const int timestep) { assert_always(fscanf(gridsave_file, "%d ", &nthreads_in) == 1); assert_always(nthreads_in == get_num_threads()); - for (int nts = 0; nts < globals::ntstep; nts++) { + for (int nts = 0; nts < globals::ntimesteps; nts++) { + int pellet_decays = 0.; assert_always(fscanf(gridsave_file, "%la %la %la %la %la %la %la %la %la %la %la %la %la %la %la %d ", - &globals::time_step[nts].gamma_dep, &globals::time_step[nts].gamma_dep_pathint, - &globals::time_step[nts].positron_dep, &globals::time_step[nts].eps_positron_ana_power, - &globals::time_step[nts].electron_dep, &globals::time_step[nts].electron_emission, - &globals::time_step[nts].eps_electron_ana_power, &globals::time_step[nts].alpha_dep, - &globals::time_step[nts].alpha_emission, &globals::time_step[nts].eps_alpha_ana_power, - &globals::time_step[nts].qdot_betaminus, &globals::time_step[nts].qdot_alpha, - &globals::time_step[nts].qdot_total, &globals::time_step[nts].gamma_emission, - &globals::time_step[nts].cmf_lum, &globals::time_step[nts].pellet_decays) == 16); + &globals::timesteps[nts].gamma_dep, &globals::timesteps[nts].gamma_dep_pathint, + &globals::timesteps[nts].positron_dep, &globals::timesteps[nts].eps_positron_ana_power, + &globals::timesteps[nts].electron_dep, &globals::timesteps[nts].electron_emission, + &globals::timesteps[nts].eps_electron_ana_power, &globals::timesteps[nts].alpha_dep, + &globals::timesteps[nts].alpha_emission, &globals::timesteps[nts].eps_alpha_ana_power, + &globals::timesteps[nts].qdot_betaminus, &globals::timesteps[nts].qdot_alpha, + &globals::timesteps[nts].qdot_total, &globals::timesteps[nts].gamma_emission, + &globals::timesteps[nts].cmf_lum, &pellet_decays) == 16); + globals::timesteps[nts].pellet_decays = pellet_decays; } int timestep_in = 0; @@ -1739,8 +1683,8 @@ static void read_grid_restart_data(const int timestep) { double rpkt_emiss = 0.; if (get_numassociatedcells(mgi) > 0) { - assert_always( - fscanf(gridsave_file, "%d %a %a %a %a %d %la", &mgi_in, &T_R, &T_e, &W, &T_J, &thick, &rpkt_emiss) == 7); + assert_always(fscanf(gridsave_file, "%d %a %a %a %a %d %la %a %a", &mgi_in, &T_R, &T_e, &W, &T_J, &thick, + &rpkt_emiss, &modelgrid[mgi].nne, &modelgrid[mgi].nnetot) == 9); if (mgi_in != mgi) { printout("[fatal] read_grid_restart_data: cell mismatch in reading input gridsave.dat ... abort\n"); @@ -1766,7 +1710,7 @@ static void read_grid_restart_data(const int timestep) { for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { - const int estimindex = mgi * get_nelements() * get_max_nions() + element * get_max_nions() + ion; + const int estimindex = get_ionestimindex(mgi, element, ion); assert_always(fscanf(gridsave_file, " %la %la", &globals::corrphotoionrenorm[estimindex], &globals::gammaestimator[estimindex]) == 2); } @@ -1790,20 +1734,20 @@ void write_grid_restart_data(const int timestep) { FILE *gridsave_file = fopen_required(filename, "w"); - fprintf(gridsave_file, "%d ", globals::ntstep); + fprintf(gridsave_file, "%d ", globals::ntimesteps); fprintf(gridsave_file, "%d ", globals::nprocs); fprintf(gridsave_file, "%d ", get_num_threads()); - for (int nts = 0; nts < globals::ntstep; nts++) { + for (int nts = 0; nts < globals::ntimesteps; nts++) { fprintf(gridsave_file, "%la %la %la %la %la %la %la %la %la %la %la %la %la %la %la %d ", - globals::time_step[nts].gamma_dep, globals::time_step[nts].gamma_dep_pathint, - globals::time_step[nts].positron_dep, globals::time_step[nts].eps_positron_ana_power, - globals::time_step[nts].electron_dep, globals::time_step[nts].electron_emission, - globals::time_step[nts].eps_electron_ana_power, globals::time_step[nts].alpha_dep, - globals::time_step[nts].alpha_emission, globals::time_step[nts].eps_alpha_ana_power, - globals::time_step[nts].qdot_betaminus, globals::time_step[nts].qdot_alpha, - globals::time_step[nts].qdot_total, globals::time_step[nts].gamma_emission, globals::time_step[nts].cmf_lum, - globals::time_step[nts].pellet_decays); + globals::timesteps[nts].gamma_dep, globals::timesteps[nts].gamma_dep_pathint, + globals::timesteps[nts].positron_dep, globals::timesteps[nts].eps_positron_ana_power, + globals::timesteps[nts].electron_dep, globals::timesteps[nts].electron_emission, + globals::timesteps[nts].eps_electron_ana_power, globals::timesteps[nts].alpha_dep, + globals::timesteps[nts].alpha_emission, globals::timesteps[nts].eps_alpha_ana_power, + globals::timesteps[nts].qdot_betaminus, globals::timesteps[nts].qdot_alpha, + globals::timesteps[nts].qdot_total, globals::timesteps[nts].gamma_emission, globals::timesteps[nts].cmf_lum, + globals::timesteps[nts].pellet_decays.load()); } fprintf(gridsave_file, "%d ", timestep); @@ -1813,15 +1757,15 @@ void write_grid_restart_data(const int timestep) { if (nonemptycell) { assert_always(globals::rpkt_emiss[mgi] >= 0.); - fprintf(gridsave_file, "%d %a %a %a %a %d %la", mgi, get_TR(mgi), get_Te(mgi), get_W(mgi), get_TJ(mgi), - modelgrid[mgi].thick, globals::rpkt_emiss[mgi]); + fprintf(gridsave_file, "%d %a %a %a %a %d %la %a %a", mgi, get_TR(mgi), get_Te(mgi), get_W(mgi), get_TJ(mgi), + modelgrid[mgi].thick, globals::rpkt_emiss[mgi], modelgrid[mgi].nne, modelgrid[mgi].nnetot); } if constexpr (USE_LUT_PHOTOION) { for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { - const int estimindex = mgi * get_nelements() * get_max_nions() + element * get_max_nions() + ion; + const int estimindex = get_ionestimindex(mgi, element, ion); fprintf(gridsave_file, " %la %la", (nonemptycell ? globals::corrphotoionrenorm[estimindex] : 0.), (nonemptycell ? globals::gammaestimator[estimindex] : 0.)); } @@ -1852,12 +1796,11 @@ static void assign_initial_temperatures() /// according to the local energy density resulting from the 56Ni decay. /// The dilution factor is W=1 in LTE. - const double tstart = globals::time_step[0].mid; + const double tstart = globals::timesteps[0].mid; + + for (int nonempymgi = 0; nonempymgi < get_nonempty_npts_model(); nonempymgi++) { + const int mgi = get_mgi_of_nonemptymgi(nonempymgi); - for (int mgi = 0; mgi < get_npts_model(); mgi++) { - if (get_numassociatedcells(mgi) == 0) { - continue; - } double decayedenergy_per_mass = decay::get_endecay_per_ejectamass_t0_to_time_withexpansion(mgi, tstart); if constexpr (INITIAL_PACKETS_ON && USE_MODEL_INITIAL_ENERGY) { decayedenergy_per_mass += get_initenergyq(mgi); @@ -1945,13 +1888,12 @@ static void setup_nstart_ndo() { assert_always(npts_nonempty_assigned == get_nonempty_npts_model()); if (globals::rank_global == 0) { - std::ofstream fileout("modelgridrankassignments.out"); + auto fileout = std::ofstream("modelgridrankassignments.out"); assert_always(fileout.is_open()); fileout << "#rank nstart ndo ndo_nonempty\n"; for (int r = 0; r < nprocesses; r++) { fileout << r << " " << ranks_nstart[r] << " " << ranks_ndo[r] << " " << ranks_ndo_nonempty[r] << "\n"; } - fileout.close(); } } @@ -1983,7 +1925,7 @@ auto get_ndo_nonempty(const int rank) -> int { return ranks_ndo_nonempty[rank]; } -static void uniform_grid_setup() +static void setup_grid_cartesian_3d() /// Routine for doing a uniform cuboidal grid. { // vmax is per coordinate, but the simulation volume corners will @@ -1993,7 +1935,7 @@ static void uniform_grid_setup() assert_always(vmax_corner < CLIGHT); /// Set grid size for uniform xyz grid - if (get_model_type() == RHO_3D_READ) { + if (get_model_type() == GRID_CARTESIAN3D) { // if we used in a 3D ejecta model, the propagation grid must match the input grid exactly ncoordgrid[0] = ncoord_model[0]; ncoordgrid[1] = ncoord_model[1]; @@ -2023,7 +1965,7 @@ static void uniform_grid_setup() for (int n = 0; n < ngrid; n++) { for (int axis = 0; axis < 3; axis++) { assert_always(nxyz[axis] == get_cellcoordpointnum(n, axis)); - cell[n].pos_min[axis] = -globals::coordmax[axis] + (2 * nxyz[axis] * globals::coordmax[axis] / ncoordgrid[axis]); + cell[n].pos_min[axis] = -globals::rmax + (2 * nxyz[axis] * globals::rmax / ncoordgrid[axis]); // cell[n].xyz[axis] = nxyz[axis]; } @@ -2041,8 +1983,8 @@ static void uniform_grid_setup() } } -static void spherical1d_grid_setup() { - assert_always(get_model_type() == RHO_1D_READ); +static void setup_grid_spherical1d() { + assert_always(get_model_type() == GRID_SPHERICAL1D); coordlabel[0] = 'r'; coordlabel[1] = '_'; coordlabel[2] = '_'; @@ -2054,11 +1996,7 @@ static void spherical1d_grid_setup() { ngrid = ncoordgrid[0] * ncoordgrid[1] * ncoordgrid[2]; cell = static_cast(malloc(ngrid * sizeof(struct gridcell))); - globals::coordmax[0] = globals::rmax; - globals::coordmax[1] = 0.; - globals::coordmax[2] = 0.; - - // in this mode, cellindex and modelgridindex are the same thing + // direct mapping, cellindex and modelgridindex are the same for (int cellindex = 0; cellindex < get_npts_model(); cellindex++) { const int mgi = cellindex; // interchangeable in this mode const double v_inner = mgi > 0 ? vout_model[mgi - 1] : 0.; @@ -2069,22 +2007,52 @@ static void spherical1d_grid_setup() { } } +static void setup_grid_cylindrical_2d() { + const double vmax_corner = sqrt(2 * pow(globals::vmax, 2)); + printout("corner vmax %g [cm/s] (%.2fc)\n", vmax_corner, vmax_corner / CLIGHT); + assert_always(vmax_corner < CLIGHT); + + assert_always(get_model_type() == GRID_CYLINDRICAL2D); + coordlabel[0] = 'r'; + coordlabel[1] = 'z'; + coordlabel[2] = '_'; + + ncoordgrid[0] = ncoord_model[0]; + ncoordgrid[1] = ncoord_model[1]; + ncoordgrid[2] = ncoord_model[2]; + + ngrid = ncoordgrid[0] * ncoordgrid[1]; + cell = static_cast(malloc(ngrid * sizeof(struct gridcell))); + + // direct mapping, cellindex and modelgridindex are the same + for (int cellindex = 0; cellindex < get_npts_model(); cellindex++) { + const int mgi = cellindex; // interchangeable in this mode + set_cell_modelgridindex(cellindex, mgi); + + const int n_rcyl = get_cellcoordpointnum(cellindex, 0); + const int n_z = get_cellcoordpointnum(cellindex, 1); + + cell[cellindex].pos_min[0] = n_rcyl * globals::rmax / ncoord_model[0]; + cell[cellindex].pos_min[1] = globals::rmax * (-1 + n_z * 2. / ncoord_model[1]); + cell[cellindex].pos_min[2] = 0.; + } +} + void grid_init(int my_rank) /// Initialises the propagation grid cells and associates them with modelgrid cells { - for (int n = 0; n <= get_npts_model(); n++) { - modelgrid[n].initial_radial_pos_sum = 0; - } - /// The cells will be ordered by x then y, then z. Call a routine that /// sets up the initial positions and widths of the cells. char grid_type_name[256] = ""; - if (GRID_TYPE == GRID_UNIFORM) { - uniform_grid_setup(); + if (GRID_TYPE == GRID_CARTESIAN3D) { + setup_grid_cartesian_3d(); strcpy(grid_type_name, "uniform cuboidal"); } else if (GRID_TYPE == GRID_SPHERICAL1D) { - spherical1d_grid_setup(); + setup_grid_spherical1d(); strcpy(grid_type_name, "spherical"); + } else if (GRID_TYPE == GRID_CYLINDRICAL2D) { + setup_grid_cylindrical_2d(); + strcpy(grid_type_name, "cylindrical"); } else { printout("[fatal] grid_init: Error: Unknown grid type. Abort."); abort(); @@ -2102,19 +2070,26 @@ void grid_init(int my_rank) // Calculate the critical opacity at which opacity_case 3 switches from a // regime proportional to the density to a regime independent of the density // This is done by solving for tau_sobolev == 1 - // tau_sobolev = PI*QE*QE/(ME*C) * rho_crit_para * rho/nucmass(28, 56) * 3000e-8 * globals::time_step[m].mid; + // tau_sobolev = PI*QE*QE/(ME*C) * rho_crit_para * rho/nucmass(28, 56) * 3000e-8 * globals::timesteps[m].mid; globals::rho_crit = ME * CLIGHT * decay::nucmass(28, 56) / (PI * QE * QE * globals::rho_crit_para * 3000e-8 * globals::tmin); printout("grid_init: rho_crit = %g [g/cm3]\n", globals::rho_crit); - if (get_model_type() == RHO_1D_READ) { - map_1dmodeltogrid(); - } else if (get_model_type() == RHO_2D_READ) { - assert_always(GRID_TYPE == GRID_UNIFORM); - map_2dmodeltogrid(); - } else if (get_model_type() == RHO_3D_READ) { - assert_always(GRID_TYPE == GRID_UNIFORM); - map_3dmodeltogrid(); + if (get_model_type() == GRID_TYPE) { + if (get_model_type() == GRID_CARTESIAN3D) { + assert_always(GRID_TYPE == GRID_CARTESIAN3D); + assert_always(ncoord_model[0] == ncoordgrid[0]); + assert_always(ncoord_model[1] == ncoordgrid[1]); + assert_always(ncoord_model[2] == ncoordgrid[2]); + } + + map_modeltogrid_direct(); + } else if (get_model_type() == GRID_SPHERICAL1D) { + assert_always(GRID_TYPE == GRID_CARTESIAN3D); + map_1dmodelto3dgrid(); + } else if (get_model_type() == GRID_CYLINDRICAL2D) { + assert_always(GRID_TYPE == GRID_CARTESIAN3D); + map_2dmodelto3dgrid(); } else { printout("[fatal] grid_init: Error: Unknown density type. Abort."); abort(); @@ -2124,7 +2099,7 @@ void grid_init(int my_rank) calculate_kappagrey(); abundances_read(); - int const ndo_nonempty = grid::get_ndo_nonempty(my_rank); + const int ndo_nonempty = grid::get_ndo_nonempty(my_rank); radfield::init(my_rank, ndo_nonempty); nonthermal::init(my_rank, ndo_nonempty); @@ -2133,15 +2108,16 @@ void grid_init(int my_rank) if (globals::simulation_continued_from_saved) { /// For continuation of an existing simulation we read the temperatures /// at the end of the simulation and write them to the grid. - read_grid_restart_data(globals::itstep); + read_grid_restart_data(globals::timestep_initial); } else { assign_initial_temperatures(); } - // when mapping 1D spherical model onto cubic grid, scale up the + // when mapping 1D spherical or 2D cylindrical model onto cubic grid, scale up the // radioactive abundances to account for the missing masses in // the model cells that are not associated with any propagation cells - if (GRID_TYPE == GRID_UNIFORM && get_model_type() == RHO_1D_READ && globals::rank_in_node == 0) { + // Luke: TODO: it's probably better to adjust the density instead of the abundances + if (GRID_TYPE == GRID_CARTESIAN3D && get_model_type() == GRID_SPHERICAL1D && globals::rank_in_node == 0) { for (int nucindex = 0; nucindex < decay::get_num_nuclides(); nucindex++) { if (totmassradionuclide[nucindex] <= 0) { continue; @@ -2168,6 +2144,14 @@ void grid_init(int my_rank) } } } + + double mtot_mapped = 0.; + for (int mgi = 0; mgi < get_npts_model(); mgi++) { + mtot_mapped += get_rho_tmin(mgi) * get_modelcell_assocvolume_tmin(mgi); + } + printout("Total grid-mapped mass: %9.3e [Msun] (%.1f%% of input mass)\n", mtot_mapped / MSUN, + mtot_mapped / mtot_input * 100.); + #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif @@ -2177,4 +2161,437 @@ auto get_totmassradionuclide(const int z, const int a) -> double { return totmassradionuclide[decay::get_nucindex(z, a)]; } +static auto get_poscoordpointnum(double pos, double time, int axis) -> int { + // pos must be position in grid coordinate system, not necessarily xyz + + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + return static_cast((pos / time + globals::vmax) / 2 / globals::vmax * ncoordgrid[axis]); + } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + if (axis == 0) { + return static_cast(pos / time / globals::vmax * ncoordgrid[axis]); + } + if (axis == 1) { + return static_cast((pos / time + globals::vmax) / 2 / globals::vmax * ncoordgrid[axis]); + } + assert_always(false); + + } else if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + for (int n_r = 0; n_r < ncoordgrid[0]; n_r++) { + if ((pos >= grid::get_cellcoordmin(n_r, 0)) && (pos < grid::get_cellcoordmax(n_r, 0))) { + return n_r; + } + } + assert_always(false); + } else { + assert_always(false); + } +} + +auto get_cellindex_from_pos(std::span pos, double time) -> int +/// identify the cell index from an (x,y,z) position and a time. +{ + double posgridcoords[3] = {NAN, NAN, NAN}; + get_gridcoords_from_xyz(pos, posgridcoords); + int cellindex = 0; + for (int d = 0; d < get_ngriddimensions(); d++) { + cellindex += get_coordcellindexincrement(d) * get_poscoordpointnum(posgridcoords[d], time, d); + } + + // do a check that the position is within the cell + const double trat = time / globals::tmin; + for (int n = 0; n < grid::get_ngriddimensions(); n++) { + assert_always(posgridcoords[n] >= grid::get_cellcoordmin(cellindex, n) * trat); + assert_always(posgridcoords[n] <= grid::get_cellcoordmax(cellindex, n) * trat); + } + return cellindex; +} + +[[nodiscard]] [[gnu::pure]] static constexpr auto expanding_shell_intersection( + std::span pos, std::span dir, const double speed, const double shellradiuststart, + const bool isinnerboundary, const double tstart) -> double +// find the closest forward distance to the intersection of a ray with an expanding spherical shell (pos and dir are +// 3-vectors) or expanding circle (2D vectors) +// returns -1 if there are no forward intersections (or if the intersection +// is tangential to the shell) +{ + assert_always(shellradiuststart > 0); + + // quadratic equation for intersection of ray with sphere + // a*d^2 + b*d + c = 0 + const double a = dot(dir, dir) - pow(shellradiuststart / tstart / speed, 2); + const double b = 2 * (dot(dir, pos) - pow(shellradiuststart, 2) / tstart / speed); + const double c = dot(pos, pos) - pow(shellradiuststart, 2); + + const double discriminant = pow(b, 2) - 4 * a * c; + + if (discriminant < 0) { + // no intersection + assert_always(isinnerboundary); + assert_always(shellradiuststart < vec_len(pos)); + return -1; + } + + if (discriminant > 0) { + // two intersections + double dist1 = (-b + sqrt(discriminant)) / 2 / a; + double dist2 = (-b - sqrt(discriminant)) / 2 / a; + + double posfinal1[3] = {0}; + double posfinal2[3] = {0}; + + for (size_t d = 0; d < pos.size(); d++) { + posfinal1[d] = pos[d] + dist1 * dir[d]; + posfinal2[d] = pos[d] + dist2 * dir[d]; + } + + const double v_rad_shell = shellradiuststart / tstart; + const double v_rad_final1 = dot(dir, posfinal1) * speed / vec_len(posfinal1); + const double v_rad_final2 = dot(dir, posfinal2) * speed / vec_len(posfinal2); + + // invalidate any solutions that require entering the boundary from the wrong radial direction + if (isinnerboundary) { + // if the packet's radial velocity at intersection is greater than the inner shell's radial velocity, + // then it is catching up from below the inner shell and should pass through it + if (v_rad_final1 > v_rad_shell) { + dist1 = -1; + } + if (v_rad_final2 > v_rad_shell) { + dist2 = -1; + } + } else { + // if the packet's radial velocity at intersection is less than the outer shell's radial velocity, + // then it is coming from above the outer shell and should pass through it + if (v_rad_final1 < v_rad_shell) { + dist1 = -1; + } + if (v_rad_final2 < v_rad_shell) { + dist2 = -1; + } + } + + if (dist1 >= 0) { + const double shellradiusfinal1 = shellradiuststart / tstart * (tstart + dist1 / speed); + assert_testmodeonly(fabs(vec_len(posfinal1) / shellradiusfinal1 - 1.) < 1e-3); + } + + if (dist2 >= 0) { + const double shellradiusfinal2 = shellradiuststart / tstart * (tstart + dist2 / speed); + assert_testmodeonly(fabs(vec_len(posfinal2) / shellradiusfinal2 - 1.) < 1e-3); + } + + // negative d means in the reverse direction along the ray + // ignore negative d values, and if two are positive then return the smaller one + if (dist1 < 0 && dist2 < 0) { + return -1; + } + if (dist2 < 0) { + return dist1; + } + if (dist1 < 0) { + return dist2; + } + return fmin(dist1, dist2); + + } // exactly one intersection + + // one intersection + // ignore this and don't change which cell the packet is in + assert_always(shellradiuststart <= vec_len(pos)); + return -1.; +} + +static auto get_coordboundary_distances_cylindrical2d(std::span pkt_pos, + std::span pkt_dir, + std::span pktposgridcoord, + std::span pktvelgridcoord, int cellindex, + const double tstart, std::span cellcoordmax, + std::span d_coordminboundary, + std::span d_coordmaxboundary) -> void { + // to get the cylindrical intersection, get the spherical intersection with Z components set to zero, and the + // propagation speed set to the xy component of the 3-velocity + + const double posnoz[2] = {pkt_pos[0], pkt_pos[1]}; + + const double dirxylen = std::sqrt((pkt_dir[0] * pkt_dir[0]) + (pkt_dir[1] * pkt_dir[1])); + const double xyspeed = dirxylen * CLIGHT_PROP; // r_cyl component of velocity + + // make a normalised direction vector in the xy plane + const double dirnoz[2] = {pkt_dir[0] / dirxylen, pkt_dir[1] / dirxylen}; + + const double r_inner = grid::get_cellcoordmin(cellindex, 0) * tstart / globals::tmin; + d_coordminboundary[0] = -1; + // don't try to calculate the intersection if the inner radius is zero + if (r_inner > 0) { + const double d_rcyl_coordminboundary = expanding_shell_intersection(posnoz, dirnoz, xyspeed, r_inner, true, tstart); + if (d_rcyl_coordminboundary >= 0) { + const double d_z_coordminboundary = d_rcyl_coordminboundary / xyspeed * pkt_dir[2] * CLIGHT_PROP; + d_coordminboundary[0] = + std::sqrt(d_rcyl_coordminboundary * d_rcyl_coordminboundary + d_z_coordminboundary * d_z_coordminboundary); + } + } + + const double r_outer = cellcoordmax[0] * tstart / globals::tmin; + const double d_rcyl_coordmaxboundary = expanding_shell_intersection(posnoz, dirnoz, xyspeed, r_outer, false, tstart); + d_coordmaxboundary[0] = -1; + if (d_rcyl_coordmaxboundary >= 0) { + const double d_z_coordmaxboundary = d_rcyl_coordmaxboundary / xyspeed * pkt_dir[2] * CLIGHT_PROP; + d_coordmaxboundary[0] = + std::sqrt(d_rcyl_coordmaxboundary * d_rcyl_coordmaxboundary + d_z_coordmaxboundary * d_z_coordmaxboundary); + } + + // z boundaries are the same as Cartesian + const double t_zcoordminboundary = + ((pktposgridcoord[1] - (pktvelgridcoord[1] * tstart)) / + ((grid::get_cellcoordmin(cellindex, 1)) - (pktvelgridcoord[1] * globals::tmin)) * globals::tmin) - + tstart; + d_coordminboundary[1] = CLIGHT_PROP * t_zcoordminboundary; + + const double t_zcoordmaxboundary = ((pktposgridcoord[1] - (pktvelgridcoord[1] * tstart)) / + ((cellcoordmax[1]) - (pktvelgridcoord[1] * globals::tmin)) * globals::tmin) - + tstart; + d_coordmaxboundary[1] = CLIGHT_PROP * t_zcoordmaxboundary; +} + +[[nodiscard]] auto boundary_distance(std::span dir, std::span pos, + const double tstart, int cellindex, int *snext, enum cell_boundary *pkt_last_cross) + -> double +/// Basic routine to compute distance to a cell boundary. +{ + if constexpr (FORCE_SPHERICAL_ESCAPE_SURFACE) { + if (get_cell_r_inner(cellindex) > globals::vmax * globals::tmin) { + *snext = -99; + return 0.; + } + } + + auto last_cross = *pkt_last_cross; + // d is used to loop over the coordinate indicies 0,1,2 for x,y,z + + // the following four vectors are in grid coordinates, so either x,y,z or r + const int ndim = grid::get_ngriddimensions(); + assert_testmodeonly(ndim <= 3); + double pktposgridcoord[3] = {0}; // pos converted from xyz to propagation grid coordinates + double cellcoordmax[3] = {0}; + double pktvelgridcoord[3] = {0}; // dir * CLIGHT_PROP converted from xyz to grid coordinates + + get_gridcoords_from_xyz(pos, pktposgridcoord); + + for (int d = 0; d < ndim; d++) { + cellcoordmax[d] = grid::get_cellcoordmax(cellindex, d); + } + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + // keep xyz Cartesian coordinates + for (int d = 0; d < ndim; d++) { + pktvelgridcoord[d] = dir[d] * CLIGHT_PROP; + } + } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + // xy plane radial velocity + pktvelgridcoord[0] = (pos[0] * dir[0] + pos[1] * dir[1]) / pktposgridcoord[0] * CLIGHT_PROP; + + // second cylindrical coordinate is z + pktvelgridcoord[1] = dir[2] * CLIGHT_PROP; + + } else if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + // the only coordinate is radius from the origin + pktvelgridcoord[0] = dot(pos, dir) / pktposgridcoord[0] * CLIGHT_PROP; // radial velocity + } else { + assert_always(false); + } + + enum cell_boundary const negdirections[3] = {COORD0_MIN, COORD1_MIN, + COORD2_MIN}; // 'X' might actually be radial coordinate + enum cell_boundary const posdirections[3] = {COORD0_MAX, COORD1_MAX, COORD2_MAX}; + + // printout("checking inside cell boundary\n"); + for (int d = 0; d < ndim; d++) { + // flip is either zero or one to indicate +ve and -ve boundaries along the selected axis + for (int flip = 0; flip < 2; flip++) { + enum cell_boundary const direction = flip != 0 ? posdirections[d] : negdirections[d]; + enum cell_boundary const invdirection = flip == 0 ? posdirections[d] : negdirections[d]; + const int cellindexstride = + flip != 0 ? -grid::get_coordcellindexincrement(d) : grid::get_coordcellindexincrement(d); + + bool isoutside_thisside = false; + double delta = 0.; + if (flip != 0) { + // packet pos below min + const double boundaryposmin = grid::get_cellcoordmin(cellindex, d) / globals::tmin * tstart; + delta = pktposgridcoord[d] - boundaryposmin; + isoutside_thisside = pktposgridcoord[d] < (boundaryposmin - 10.); // 10 cm accuracy tolerance + } else { + // packet pos above max + const double boundaryposmax = cellcoordmax[d] / globals::tmin * tstart; + delta = pktposgridcoord[d] - boundaryposmax; + isoutside_thisside = pktposgridcoord[d] > (boundaryposmax + 10.); + } + + if (isoutside_thisside && (last_cross != direction)) { + // for (int d2 = 0; d2 < ndim; d2++) + const int d2 = d; + { + printout( + "[warning] packet outside coord %d %c%c boundary of cell %d. vel %g initpos %g " + "cellcoordmin %g, cellcoordmax %g\n", + d, flip != 0 ? '-' : '+', grid::coordlabel[d], cellindex, pktvelgridcoord[d2], pktposgridcoord[d2], + grid::get_cellcoordmin(cellindex, d2) / globals::tmin * tstart, + cellcoordmax[d2] / globals::tmin * tstart); + } + printout("globals::tmin %g tstart %g tstart/globals::tmin %g\n", globals::tmin, tstart, tstart / globals::tmin); + printout("[warning] delta %g\n", delta); + + printout("[warning] dir [%g, %g, %g]\n", dir[0], dir[1], dir[2]); + if ((pktvelgridcoord[d] - (pktposgridcoord[d] / tstart)) > 0) { + if ((grid::get_cellcoordpointnum(cellindex, d) == (grid::ncoordgrid[d] - 1) && cellindexstride > 0) || + (grid::get_cellcoordpointnum(cellindex, d) == 0 && cellindexstride < 0)) { + printout("escaping packet\n"); + *snext = -99; + return 0; + } + *snext = cellindex + cellindexstride; + *pkt_last_cross = invdirection; + printout("[warning] swapping packet cellindex from %d to %d and setting last_cross to %d\n", cellindex, + *snext, *pkt_last_cross); + return 0; + } + printout("pretending last_cross is %d\n", direction); + last_cross = direction; + } + } + } + + // printout("pkt_ptr->number %d\n", pkt_ptr->number); + // printout("delta1x %g delta2x %g\n", (initpos[0] * globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 0), + // cellcoordmax[0] - (initpos[0] * globals::tmin/tstart)); printout("delta1y %g delta2y %g\n", (initpos[1] * + // globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 1), cellcoordmax[1] - (initpos[1] * + // globals::tmin/tstart)); printout("delta1z %g delta2z %g\n", (initpos[2] * + // globals::tmin/tstart)-grid::get_cellcoordmin(cellindex, 2), cellcoordmax[2] - (initpos[2] * + // globals::tmin/tstart)); printout("dir [%g, %g, %g]\n", dir[0],dir[1],dir[2]); + + double d_coordmaxboundary[3] = {-1}; // distance to reach the cell's upper boundary on each coordinate + double d_coordminboundary[3] = {-1}; // distance to reach the cell's lower boundary on each coordinate + if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + last_cross = BOUNDARY_NONE; // handle this separately by setting d_inner and d_outer negative for invalid direction + const double speed = vec_len(dir) * CLIGHT_PROP; // just in case dir is not normalised + + const double r_inner = grid::get_cellcoordmin(cellindex, 0) * tstart / globals::tmin; + d_coordminboundary[0] = (r_inner > 0.) ? expanding_shell_intersection(pos, dir, speed, r_inner, true, tstart) : -1.; + + const double r_outer = cellcoordmax[0] * tstart / globals::tmin; + d_coordmaxboundary[0] = expanding_shell_intersection(pos, dir, speed, r_outer, false, tstart); + + } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + // coordinate 0 is radius in x-y plane, coord 1 is z + if (last_cross == COORD0_MIN || last_cross == COORD0_MAX) { + last_cross = + BOUNDARY_NONE; // handle this separately by setting d_inner and d_outer negative for invalid direction + } + + get_coordboundary_distances_cylindrical2d(pos, dir, pktposgridcoord, pktvelgridcoord, cellindex, tstart, + cellcoordmax, d_coordminboundary, d_coordmaxboundary); + + } else if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + // There are six possible boundary crossings. Each of the three + // cartesian coordinates may be taken in turn. For x, the packet + // trajectory is + // x = x0 + (dir.x) * c * (t - tstart) + // the boundries follow + // x+/- = x+/-(tmin) * (t/tmin) + // so the crossing occurs when + // t = (x0 - (dir.x)*c*tstart)/(x+/-(tmin)/tmin - (dir.x)c) + + // Modified so that it also returns the distance to the closest cell + // boundary, regardless of direction. + + for (int d = 0; d < 3; d++) { + const double t_coordminboundary = + ((pktposgridcoord[d] - (pktvelgridcoord[d] * tstart)) / + (grid::get_cellcoordmin(cellindex, d) - (pktvelgridcoord[d] * globals::tmin)) * globals::tmin) - + tstart; + d_coordminboundary[d] = CLIGHT_PROP * t_coordminboundary; + + const double t_coordmaxboundary = ((pktposgridcoord[d] - (pktvelgridcoord[d] * tstart)) / + (cellcoordmax[d] - (pktvelgridcoord[d] * globals::tmin)) * globals::tmin) - + tstart; + + d_coordmaxboundary[d] = CLIGHT_PROP * t_coordmaxboundary; + } + } else { + assert_always(false); + } + + // We now need to identify the shortest +ve distance - that's the one we want. + enum cell_boundary choice = BOUNDARY_NONE; + double distance = std::numeric_limits::max(); + for (int d = 0; d < ndim; d++) { + // upper d coordinate of the current cell + if ((d_coordmaxboundary[d] > 0) && (d_coordmaxboundary[d] < distance) && (last_cross != negdirections[d])) { + choice = posdirections[d]; + distance = d_coordmaxboundary[d]; + if (grid::get_cellcoordpointnum(cellindex, d) == (grid::ncoordgrid[d] - 1)) { + *snext = -99; + } else { + *pkt_last_cross = choice; + *snext = cellindex + grid::get_coordcellindexincrement(d); + } + } + + // lower d coordinate of the current cell + if ((d_coordminboundary[d] > 0) && (d_coordminboundary[d] < distance) && (last_cross != posdirections[d])) { + choice = negdirections[d]; + distance = d_coordminboundary[d]; + if (grid::get_cellcoordpointnum(cellindex, d) == 0) { + *snext = -99; + } else { + *pkt_last_cross = choice; + *snext = cellindex - grid::get_coordcellindexincrement(d); + } + } + } + + if (choice == BOUNDARY_NONE) { + printout("Something wrong in boundary crossing - didn't find anything.\n"); + printout("packet cell %d\n", cellindex); + printout("choice %d\n", choice); + printout("globals::tmin %g tstart %g\n", globals::tmin, tstart); + printout("last_cross %d\n", last_cross); + for (int d2 = 0; d2 < 3; d2++) { + printout("coord %d: initpos %g dir %g\n", d2, pos[d2], dir[d2]); + } + printout("|initpos| %g |dir| %g |pos.dir| %g\n", vec_len(pos), vec_len(dir), dot(pos, dir)); + for (int d2 = 0; d2 < ndim; d2++) { + printout("coord %d: dist_posmax %g dist_posmin %g \n", d2, d_coordmaxboundary[d2], d_coordminboundary[d2]); + printout("coord %d: cellcoordmin %g cellcoordmax %g\n", d2, + grid::get_cellcoordmin(cellindex, d2) * tstart / globals::tmin, + cellcoordmax[d2] * tstart / globals::tmin); + } + printout("tstart %g\n", tstart); + + assert_always(false); + } + + return distance; +} + +void change_cell(struct packet *pkt_ptr, int snext) +/// Routine to take a packet across a boundary. +{ + // const int cellindex = pkt_ptr->where; + // printout("[debug] cellnumber %d nne %g\n", cellindex, grid::get_nne(grid::get_cell_modelgridindex(cellindex))); + // printout("[debug] snext %d\n", snext); + + if (snext == -99) { + // Then the packet is exiting the grid. We need to record + // where and at what time it leaves the grid. + pkt_ptr->escape_type = pkt_ptr->type; + pkt_ptr->escape_time = pkt_ptr->prop_time; + pkt_ptr->type = TYPE_ESCAPE; + globals::nesc++; + } else { + // Just need to update "where". + pkt_ptr->where = snext; + + stats::increment(stats::COUNTER_CELLCROSSINGS); + } +} + } // namespace grid diff --git a/grid.h b/grid.h index 628527b3d..80d0834b2 100644 --- a/grid.h +++ b/grid.h @@ -2,9 +2,12 @@ #define GRIDINIT_H #include +#include #include "artisoptions.h" #include "constants.h" +#include "packet.h" +#include "vectors.h" namespace grid { @@ -21,13 +24,6 @@ struct gridcell { int modelgridindex; }; -enum model_types { - RHO_UNIFORM = 1, // Constant density. NOT IN USE - RHO_1D_READ = 2, // Read model 1D - RHO_2D_READ = 4, // Read model 2D - RHO_3D_READ = 3, // Read model 3D -}; - struct modelgrid_t { float Te = -1.; float TR = -1.; @@ -62,7 +58,17 @@ struct modelgrid_t { uint_fast8_t thick = 0; }; -constexpr int get_ngriddimensions(void) { return (GRID_TYPE == GRID_SPHERICAL1D) ? 1 : 3; } +constexpr int get_ngriddimensions(void) { + switch (GRID_TYPE) { + case GRID_SPHERICAL1D: + return 1; + case GRID_CYLINDRICAL2D: + return 2; + case GRID_CARTESIAN3D: + return 3; + } + assert_always(false); +} extern struct modelgrid_t *modelgrid; @@ -72,7 +78,7 @@ extern char coordlabel[3]; int get_elements_uppermost_ion(int modelgridindex, int element); void set_elements_uppermost_ion(int modelgridindex, int element, int newvalue); -double wid_init(int cellindex); +double wid_init(int cellindex, int axis); double get_modelcell_assocvolume_tmin(int modelgridindex); double get_gridcell_volume_tmin(int cellindex); double get_cellcoordmax(int cellindex, int axis); @@ -96,12 +102,12 @@ float get_W(int modelgridindex); void set_nne(int modelgridindex, float nne); void set_nnetot(int modelgridindex, float x); void set_kappagrey(int modelgridindex, float kappagrey); +void set_rho(int modelgridindex, float x); void set_Te(int modelgridindex, float Te); void set_TR(int modelgridindex, float TR); void set_TJ(int modelgridindex, float TJ); void set_W(int modelgridindex, float W); void grid_init(int my_rank); -double get_cellradialpos(int cellindex); float get_modelinitradioabund(int modelgridindex, int nucindex); float get_stable_initabund(int mgi, int element); float get_element_meanweight(int mgi, int element); @@ -110,12 +116,13 @@ double get_electronfrac(int modelgridindex); int get_numassociatedcells(int modelgridindex); int get_modelcell_nonemptymgi(int mgi); int get_mgi_of_nonemptymgi(int nonemptymgi); -enum model_types get_model_type(); -void set_model_type(enum model_types model_type_value); +enum gridtypes get_model_type(); +void set_model_type(enum gridtypes model_type_value); int get_npts_model(); int get_nonempty_npts_model(); double get_t_model(); int get_cell_modelgridindex(int cellindex); +int get_cellindex_from_pos(std::span pos, double time); void read_ejecta_model(); void write_grid_restart_data(int timestep); int get_maxndo(); @@ -123,6 +130,9 @@ int get_nstart(int rank); int get_ndo(int rank); int get_ndo_nonempty(int rank); double get_totmassradionuclide(int z, int a); +double boundary_distance(std::span dir, std::span pos, double tstart, int cellindex, + int *snext, enum cell_boundary *pkt_last_cross); +void change_cell(struct packet *pkt_ptr, int snext); static inline float get_elem_abundance(int modelgridindex, int element) // mass fraction of an element (all isotopes combined) @@ -130,6 +140,22 @@ static inline float get_elem_abundance(int modelgridindex, int element) return modelgrid[modelgridindex].composition[element].abundance; } +constexpr auto get_gridcoords_from_xyz(std::span pos_xyz, std::span posgridcoord) -> void { + if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { + posgridcoord[0] = pos_xyz[0]; + posgridcoord[1] = pos_xyz[1]; + posgridcoord[2] = pos_xyz[2]; + } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + posgridcoord[0] = std::sqrt(std::pow(pos_xyz[0], 2) + std::pow(pos_xyz[1], 2)); + posgridcoord[1] = pos_xyz[2]; + posgridcoord[2] = 0.; + } else if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + posgridcoord[0] = vec_len(pos_xyz); + posgridcoord[1] = 0.; + posgridcoord[2] = 0.; + } +} + } // namespace grid #endif // GRIDINIT_H diff --git a/input.cc b/input.cc index fc51b77d9..fcaacbb9b 100644 --- a/input.cc +++ b/input.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include "grid.h" #include "kpkt.h" #include "nltepop.h" +#include "ratecoeff.h" #include "rpkt.h" #include "sn3d.h" #include "vpkt.h" @@ -34,15 +36,15 @@ struct transitions { struct transitiontable_entry { int lower; int upper; - double A; - double coll_str; + float A; + float coll_str; bool forbidden; }; /// only used temporarily during input -const std::array inputlinecomments = { +constexpr std::array inputlinecomments = { " 0: pre_zseed: specific random number seed if > 0 or random if negative", - " 1: globals::ntstep: number of timesteps", - " 2: itstep ftstep: timestep number range start (inclusive) and stop (not inclusive)", + " 1: ntimesteps: number of timesteps", + " 2: timestep_start timestep_finish: timestep number range start (inclusive) and stop (not inclusive)", " 3: tmin_days tmax_days: start and end times [day]", " 4: UNUSED nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV]", " 5: UNUSED nsyn_time: number of times for synthesis", @@ -51,7 +53,7 @@ const std::array inputlinecomments = { " 8: UNUSED compute r-light curve (1: no estimators, 2: thin cells, 3: thick cells, 4: gamma-ray heating)", " 9: UNUSED n_out_it: number of iterations", "10: UNUSED: change speed of light by some factor. Change constants.h CLIGHT_PROP instead", - "11: use grey opacity for gammas?", + "11: gamma_kappagrey: if >0: use grey opacity for gammas, if <0: use detailed opacity", "12: syn_dir: x, y, and z components of unit vector (will be normalised after input or randomised if zero length)", "13: opacity_case: opacity choice", "14: rho_crit_para: free parameter for calculation of rho_crit", @@ -68,7 +70,7 @@ const std::array inputlinecomments = { "23: kpktdiffusion_timescale n_kpktdiffusion_timesteps: kpkts diffuse x of a time step's length for the first y " "time steps"}; -static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputtable, const int element, +static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoints_inputtable, const int element, const int lowerion, const int lowerlevel, const int upperion, int upperlevel_in, const double phixs_threshold_ev, size_t *mem_usage_phixs) { if (upperlevel_in >= 0) // file gives photoionisation to a single target state only @@ -94,7 +96,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt } else // upperlevel < 0, indicating that a table of upper levels and their probabilities will follow { int in_nphixstargets = 0; - assert_always(fscanf(phixsdata, "%d\n", &in_nphixstargets) == 1); + assert_always(phixsfile >> in_nphixstargets); assert_always(in_nphixstargets >= 0); // read in a table of target states and probabilities and store them if (!single_level_top_ion || upperion < get_nions(element) - 1) // in case the top ion has nlevelsmax = 1 @@ -109,7 +111,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt double probability_sum = 0.; for (int i = 0; i < in_nphixstargets; i++) { double phixstargetprobability = NAN; - assert_always(fscanf(phixsdata, "%d %lg\n", &upperlevel_in, &phixstargetprobability) == 2); + assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); const int upperlevel = upperlevel_in - groundstate_index_in; assert_always(upperlevel >= 0); assert_always(phixstargetprobability > 0); @@ -132,7 +134,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt for (int i = 0; i < in_nphixstargets; i++) { double phixstargetprobability = NAN; - assert_always(fscanf(phixsdata, "%d %lg\n", &upperlevel_in, &phixstargetprobability) == 2); + assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); } // send it to the ground state of the top ion @@ -177,7 +179,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt assert_always(get_nphixstargets(element, lowerion, lowerlevel) == 1); assert_always(get_phixsupperlevel(element, lowerion, lowerlevel, 0) == 0); - double const nu_edge = (epsilon(element, upperion, 0) - epsilon(element, lowerion, lowerlevel)) / H; + const double nu_edge = (epsilon(element, upperion, 0) - epsilon(element, lowerion, lowerlevel)) / H; auto *nutable = static_cast(calloc(nphixspoints_inputtable, sizeof(double))); assert_always(nutable != nullptr); @@ -187,7 +189,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt for (int i = 0; i < nphixspoints_inputtable; i++) { double energy = -1.; double phixs = -1.; - assert_always(fscanf(phixsdata, "%lg %lg", &energy, &phixs) == 2); + assert_always(phixsfile >> energy >> phixs); nutable[i] = nu_edge + (energy * 13.6 * EV) / H; /// the photoionisation cross-sections in the database are given in Mbarn=1e6 * 1e-28m^2 /// to convert to cgs units multiply by 1e-18 @@ -218,7 +220,7 @@ static void read_phixs_data_table(FILE *phixsdata, const int nphixspoints_inputt } else { for (int i = 0; i < globals::NPHIXSPOINTS; i++) { float phixs = NAN; - assert_always(fscanf(phixsdata, "%g\n", &phixs) == 1); + assert_always(phixsfile >> phixs); assert_always(phixs >= 0); /// the photoionisation cross-sections in the database are given in Mbarn = 1e6 * 1e-28m^2 @@ -241,7 +243,7 @@ static void read_phixs_data(const int phixs_file_version) { printout("readin phixs data from %s\n", phixsdata_filenames[phixs_file_version]); - FILE *phixsdata = fopen_required(phixsdata_filenames[phixs_file_version], "r"); + auto phixsfile = fstream_required(phixsdata_filenames[phixs_file_version], std::ios::in); if (phixs_file_version == 1 && phixs_file_version_exists[2]) { printout( @@ -257,9 +259,9 @@ static void read_phixs_data(const int phixs_file_version) { printout("using NPHIXSPOINTS = %d and NPHIXSNUINCREMENT = %lg set in input.cc\n", globals::NPHIXSPOINTS, globals::NPHIXSNUINCREMENT); } else { - assert_always(fscanf(phixsdata, "%d\n", &globals::NPHIXSPOINTS) == 1); + assert_always(phixsfile >> globals::NPHIXSPOINTS); assert_always(globals::NPHIXSPOINTS > 0); - assert_always(fscanf(phixsdata, "%lg\n", &globals::NPHIXSNUINCREMENT) == 1); + assert_always(phixsfile >> globals::NPHIXSNUINCREMENT); assert_always(globals::NPHIXSNUINCREMENT > 0.); last_phixs_nuovernuedge = (1.0 + globals::NPHIXSNUINCREMENT * (globals::NPHIXSPOINTS - 1)); } @@ -271,20 +273,19 @@ static void read_phixs_data(const int phixs_file_version) { int lowerlevel_in = -1; double phixs_threshold_ev = -1; while (true) { - int readstatus = -1; int nphixspoints_inputtable = 0; + std::string phixsline; + if (!get_noncommentline(phixsfile, phixsline)) { + break; + } if (phixs_file_version == 1) { - readstatus = fscanf(phixsdata, "%d %d %d %d %d %d\n", &Z, &upperionstage, &upperlevel_in, &lowerionstage, - &lowerlevel_in, &nphixspoints_inputtable); + assert_always(std::istringstream(phixsline) >> Z >> upperionstage >> upperlevel_in >> lowerionstage >> + lowerlevel_in >> nphixspoints_inputtable); } else { - readstatus = fscanf(phixsdata, "%d %d %d %d %d %lg\n", &Z, &upperionstage, &upperlevel_in, &lowerionstage, - &lowerlevel_in, &phixs_threshold_ev); + assert_always(std::istringstream(phixsline) >> Z >> upperionstage >> upperlevel_in >> lowerionstage >> + lowerlevel_in >> phixs_threshold_ev); nphixspoints_inputtable = globals::NPHIXSPOINTS; } - if (readstatus == EOF) { - break; - } - assert_always(readstatus == 6); assert_always(Z > 0); assert_always(upperionstage >= 2); assert_always(lowerionstage >= 1); @@ -296,7 +297,7 @@ static void read_phixs_data(const int phixs_file_version) { /// store only photoionization crosssections for elements that are part of the current model atom skip_this_phixs_table = true; // will be set to false for good data - if (element >= 0) { + if (element >= 0 && get_nions(element) > 0) { /// translate readin ionstages to ion indices const int upperion = upperionstage - get_ionstage(element, 0); @@ -306,7 +307,7 @@ static void read_phixs_data(const int phixs_file_version) { assert_always(lowerlevel >= 0); /// store only photoionization crosssections for ions that are part of the current model atom if (lowerion >= 0 && upperion < get_nions(element) && lowerlevel < get_nlevels(element, lowerion)) { - read_phixs_data_table(phixsdata, nphixspoints_inputtable, element, lowerion, lowerlevel, upperion, + read_phixs_data_table(phixsfile, nphixspoints_inputtable, element, lowerion, lowerlevel, upperion, upperlevel_in, phixs_threshold_ev, &mem_usage_phixs); skip_this_phixs_table = false; @@ -319,10 +320,10 @@ static void read_phixs_data(const int phixs_file_version) { if (upperlevel_in < 0) // a table of target states and probabilities will follow, so read past those lines { int nphixstargets = 0; - assert_always(fscanf(phixsdata, "%d\n", &nphixstargets) == 1); + assert_always(phixsfile >> nphixstargets); for (int i = 0; i < nphixstargets; i++) { double phixstargetprobability = NAN; - assert_always(fscanf(phixsdata, "%d %lg\n", &upperlevel_in, &phixstargetprobability) == 2); + assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); } } for (int i = 0; i < nphixspoints_inputtable; i++) // skip through cross section list @@ -330,20 +331,18 @@ static void read_phixs_data(const int phixs_file_version) { float phixs = 0; if (phixs_file_version == 1) { double energy = 0; - assert_always(fscanf(phixsdata, "%lg %g\n", &energy, &phixs) == 2); + assert_always(phixsfile >> energy >> phixs); } else { - assert_always(fscanf(phixsdata, "%g\n", &phixs) == 1); + assert_always(phixsfile >> phixs); } } } } - fclose(phixsdata); - printout("[info] mem_usage: photoionisation tables occupy %.3f MB\n", mem_usage_phixs / 1024. / 1024.); } -static void read_ion_levels(FILE *adata, const int element, const int ion, const int nions, const int nlevels, +static void read_ion_levels(std::fstream &adata, const int element, const int ion, const int nions, const int nlevels, int nlevelsmax, const double energyoffset, const double ionpot, struct transitions *transitions) { const int nlevels_used = std::min(nlevels, nlevelsmax); @@ -357,8 +356,9 @@ static void read_ion_levels(FILE *adata, const int element, const int ion, const double levelenergy = NAN; double statweight = NAN; int ntransitions = 0; - assert_always(fscanf(adata, "%d %lg %lg %d%*[^\n]\n", &levelindex_in, &levelenergy, &statweight, &ntransitions) == - 4); + std::string line; + assert_always(get_noncommentline(adata, line)); + assert_always(std::istringstream(line) >> levelindex_in >> levelenergy >> statweight >> ntransitions); assert_always(levelindex_in == level + groundstate_index_in); if (level < nlevelsmax) { @@ -402,10 +402,13 @@ static void read_ion_levels(FILE *adata, const int element, const int ion, const } } -static void read_ion_transitions(std::istream &ftransitiondata, const int tottransitions_in_file, int *tottransitions, +static void read_ion_transitions(std::fstream &ftransitiondata, const int tottransitions_in_file, int *tottransitions, std::vector &transitiontable, const int nlevels_requiretransitions, const int nlevels_requiretransitions_upperlevels) { + transitiontable.reserve(*tottransitions); + transitiontable.clear(); + std::string line; if (*tottransitions == 0) { @@ -422,26 +425,25 @@ static void read_ion_transitions(std::istream &ftransitiondata, const int tottra for (int i = 0; i < tottransitions_in_file; i++) { int lower_in = -1; int upper_in = -1; - double A = 0; - double coll_str = -1.; + float A = 0; + float coll_str = -1.; int intforbidden = 0; assert_always(getline(ftransitiondata, line)); if (i == 0) { - std::stringstream ss(line); + std::istringstream ss(line); std::string word; - int word_count = 0; + int column_count = 0; while (ss >> word) { - word_count++; + column_count++; } - assert_always(word_count == 4 || word_count == 5); - oldtransitionformat = (word_count == 4); + assert_always(column_count == 4 || column_count == 5); + oldtransitionformat = (column_count == 4); } if (!oldtransitionformat) { - assert_always(sscanf(line.c_str(), "%d %d %lg %lg %d", &lower_in, &upper_in, &A, &coll_str, &intforbidden) == - 5); + assert_always(std::istringstream(line) >> lower_in >> upper_in >> A >> coll_str >> intforbidden); } else { int transindex = 0; // not used - assert_always(sscanf(line.c_str(), "%d %d %d %lg", &transindex, &lower_in, &upper_in, &A) == 4); + assert_always(std::istringstream(line) >> transindex >> lower_in >> upper_in >> A); } const int lower = lower_in - groundstate_index_in; const int upper = upper_in - groundstate_index_in; @@ -451,6 +453,7 @@ static void read_ion_transitions(std::istream &ftransitiondata, const int tottra // this entire block can be removed if we don't want to add in extra collisonal // transitions between levels if (prev_lower < nlevels_requiretransitions) { + assert_always(prev_lower >= 0); int stoplevel = 0; if (lower == prev_lower && upper > prev_upper + 1) { // same lower level, but some upper levels were skipped over @@ -472,7 +475,6 @@ static void read_ion_transitions(std::istream &ftransitiondata, const int tottra // printout("+adding transition index %d Z=%02d ionstage %d lower %d upper %d\n", i, Z, ionstage, prev_lower, // tmplevel); (*tottransitions)++; - assert_always(prev_lower >= 0); assert_always(tmplevel >= 0); transitiontable.push_back( {.lower = prev_lower, .upper = tmplevel, .A = 0., .coll_str = -2., .forbidden = true}); @@ -574,11 +576,11 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io totupdowntrans += 2; if (pass == 1 && globals::rank_in_node == 0) { - const double A_ul = transitiontable[ii].A; + const float A_ul = transitiontable[ii].A; const float coll_str = transitiontable[ii].coll_str; - const double g = stat_weight(element, ion, level) / stat_weight(element, ion, targetlevel); - const float f_ul = g * ME * pow(CLIGHT, 3) / (8 * pow(QE * nu_trans * PI, 2)) * A_ul; + const auto g_ratio = stat_weight(element, ion, level) / stat_weight(element, ion, targetlevel); + const float f_ul = g_ratio * ME * pow(CLIGHT, 3) / (8 * pow(QE * nu_trans * PI, 2)) * A_ul; assert_always(std::isfinite(f_ul)); // printout("lineindex %d, element %d, ion %d, lower %d, upper %d, nu @@ -586,7 +588,7 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io temp_linelist.push_back({ .nu = nu_trans, - .einstein_A = static_cast(A_ul), + .einstein_A = A_ul, .elementindex = element, .ionindex = ion, .upperlevelindex = level, @@ -620,11 +622,11 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io // This is a new branch to deal with lines that have different types of transition. It should trip after a // transition is already known. const int linelistindex = transitions[level].to[level - targetlevel - 1]; - const double A_ul = transitiontable[ii].A; + const float A_ul = transitiontable[ii].A; const float coll_str = transitiontable[ii].coll_str; - const double g = stat_weight(element, ion, level) / stat_weight(element, ion, targetlevel); - const float f_ul = g * ME * pow(CLIGHT, 3) / (8 * pow(QE * nu_trans * PI, 2)) * A_ul; + const auto g_ratio = stat_weight(element, ion, level) / stat_weight(element, ion, targetlevel); + const float f_ul = g_ratio * ME * pow(CLIGHT, 3) / (8 * pow(QE * nu_trans * PI, 2)) * A_ul; if ((temp_linelist[linelistindex].elementindex != element) || (temp_linelist[linelistindex].ionindex != ion) || (temp_linelist[linelistindex].upperlevelindex != level) || @@ -647,12 +649,12 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io const int nloweruptrans = get_nuptrans(element, ion, targetlevel) + 1; auto &downtransition = globals::elements[element].ions[ion].levels[level].downtrans[nupperdowntrans - 1]; - downtransition.einstein_A += static_cast(A_ul); + downtransition.einstein_A += A_ul; downtransition.osc_strength += f_ul; downtransition.coll_str = std::max(downtransition.coll_str, coll_str); auto &uptransition = globals::elements[element].ions[ion].levels[targetlevel].uptrans[nloweruptrans - 1]; - uptransition.einstein_A += static_cast(A_ul); + uptransition.einstein_A += A_ul; uptransition.osc_strength += f_ul; uptransition.coll_str = std::max(uptransition.coll_str, coll_str); } @@ -708,35 +710,32 @@ static void read_atomicdata_files() { int totaluptrans = 0; int totaldowntrans = 0; - FILE *compositiondata = fopen_required("compositiondata.txt", "r"); + auto compositiondata = fstream_required("compositiondata.txt", std::ios::in); - FILE *adata = fopen_required("adata.txt", "r"); + auto adata = fstream_required("adata.txt", std::ios::in); printout("single_level_top_ion: %s\n", single_level_top_ion ? "true" : "false"); printout("single_ground_level: %s\n", single_ground_level ? "true" : "false"); /// initialize atomic data structure to number of elements int nelements_in = 0; - assert_always(fscanf(compositiondata, "%d", &nelements_in) == 1); + assert_always(compositiondata >> nelements_in); set_nelements(nelements_in); - globals::elements = static_cast(calloc(get_nelements(), sizeof(elementlist_entry))); - assert_always(globals::elements != nullptr); /// Initialize the linelist std::vector temp_linelist; + std::vector transitiontable; + /// temperature to determine relevant ionstages int T_preset = 0; - assert_always(fscanf(compositiondata, "%d", &T_preset) == 1); - int homogeneous_abundances_in = 0; - assert_always(fscanf(compositiondata, "%d", &homogeneous_abundances_in) == 1); - globals::homogeneous_abundances = (homogeneous_abundances_in != 0); - if (globals::homogeneous_abundances) { - printout("[info] read_atomicdata: homogeneous abundances as defined in compositiondata.txt are active\n"); - } + assert_always(compositiondata >> T_preset); + assert_always(T_preset == 0); // no longer in use + int homogeneous_abundances = 0; + assert_always(compositiondata >> homogeneous_abundances); + assert_always(homogeneous_abundances == 0); // no longer in use /// open transition data file - std::ifstream ftransitiondata("transitiondata.txt"); - assert_always(ftransitiondata.is_open()); + auto ftransitiondata = fstream_required("transitiondata.txt", std::ios::in); int lineindex = 0; /// counter to determine the total number of lines int uniqueionindex = 0; // index into list of all ions of all elements @@ -751,8 +750,8 @@ static void read_atomicdata_files() { int nlevelsmax_readin = 0; double abundance = NAN; double mass_amu = NAN; - assert_always(fscanf(compositiondata, "%d %d %d %d %d %lg %lg", &Z, &nions, &lowermost_ionstage, - &uppermost_ionstage, &nlevelsmax_readin, &abundance, &mass_amu) == 7); + assert_always(compositiondata >> Z >> nions >> lowermost_ionstage >> uppermost_ionstage >> nlevelsmax_readin >> + abundance >> mass_amu); printout("readin compositiondata: next element Z %d, nions %d, lowermost %d, uppermost %d, nlevelsmax %d\n", Z, nions, lowermost_ionstage, uppermost_ionstage, nlevelsmax_readin); assert_always(Z > 0); @@ -761,15 +760,11 @@ static void read_atomicdata_files() { assert_always(abundance >= 0); assert_always(mass_amu >= 0); - update_max_nions(nions); - assert_always(nions <= get_max_nions()); - /// write this element's data to memory globals::elements[element].anumber = Z; globals::elements[element].nions = nions; globals::elements[element].abundance = abundance; /// abundances are expected to be given by mass globals::elements[element].initstablemeannucmass = mass_amu * MH; - increase_includedions(nions); /// Initialize the elements ionlist globals::elements[element].ions = static_cast(calloc(nions, sizeof(ionlist_entry))); @@ -803,11 +798,15 @@ static void read_atomicdata_files() { double statweight = NAN; int levelindex = 0; int ntransitions = 0; - assert_always( - fscanf(adata, "%d %lg %lg %d%*[^\n]\n", &levelindex, &levelenergy, &statweight, &ntransitions) == 4); + std::string line; + std::getline(adata, line); + + assert_always(std::istringstream(line) >> levelindex >> levelenergy >> statweight >> ntransitions); } - assert_always(fscanf(adata, "%d %d %d %lg\n", &adata_Z_in, &ionstage, &nlevels, &ionpot) == 4); + std::string line; + assert_always(get_noncommentline(adata, line)); + assert_always(std::istringstream(line) >> adata_Z_in >> ionstage >> nlevels >> ionpot); } printout("adata header matched: Z %d, ionstage %d, nlevels %d\n", adata_Z_in, ionstage, nlevels); @@ -845,9 +844,8 @@ static void read_atomicdata_files() { for (int i = 0; i < tottransitions_in_file; i++) { assert_always(getline(ftransitiondata, line)); } - assert_always(get_noncommentline(ftransitiondata, line)); - assert_always( - sscanf(line.c_str(), "%d %d %d", &transdata_Z_in, &transdata_ionstage_in, &tottransitions_in_file) == 3); + assert_always(get_noncommentline(ftransitiondata, line)); // get_noncommentline to skip over blank lines + assert_always(std::istringstream(line) >> transdata_Z_in >> transdata_ionstage_in >> tottransitions_in_file); } printout("transdata header matched: transdata_Z_in %d, transdata_ionstage_in %d, tottransitions %d\n", @@ -863,6 +861,7 @@ static void read_atomicdata_files() { globals::elements[element].ions[ion].ionpot = ionpot * EV; globals::elements[element].ions[ion].nlevels_groundterm = -1; globals::elements[element].ions[ion].uniqueionindex = uniqueionindex; + globals::elements[element].ions[ion].first_nlte = -1; globals::elements[element].ions[ion].Alpha_sp = static_cast(calloc(TABLESIZE, sizeof(float))); assert_always(globals::elements[element].ions[ion].Alpha_sp != nullptr); @@ -886,10 +885,6 @@ static void read_atomicdata_files() { assert_always(transdata_Z_in == Z); assert_always(transdata_ionstage_in == ionstage); - /// read in the level and transition data for this ion - std::vector transitiontable; - transitiontable.reserve(tottransitions); - /// load transition table for the CURRENT ion to temporary memory // first levels will be collisionally @@ -929,9 +924,6 @@ static void read_atomicdata_files() { uniqueionindex++; } } - fclose(adata); - ftransitiondata.close(); - fclose(compositiondata); printout("nbfcheck %d\n", nbfcheck); /// Save the linecounters value to the global variable containing the number of lines @@ -1028,10 +1020,10 @@ static void read_atomicdata_files() { // fclose(linelist_file); // } - printout("establish connection between transitions and sorted linelist..."); + printout("establishing connection between transitions and sorted linelist...\n"); time_t const time_start_establish_linelist_connections = time(nullptr); - for (int lineindex = 0; lineindex < globals::nlines; lineindex++) { + for (lineindex = 0; lineindex < globals::nlines; lineindex++) { const auto &line = globals::linelist[lineindex]; const int element = line.elementindex; const int ion = line.ionindex; @@ -1044,7 +1036,7 @@ static void read_atomicdata_files() { const int nupperdowntrans = get_ndowntrans(element, ion, upperlevel); auto *downtranslist = globals::elements[element].ions[ion].levels[upperlevel].downtrans; auto *downtrans = std::find_if(downtranslist, downtranslist + nupperdowntrans, - [=](auto &downtrans) { return downtrans.targetlevelindex == lowerlevel; }); + [=](const auto &downtrans) { return downtrans.targetlevelindex == lowerlevel; }); assert_always(downtrans != (downtranslist + nupperdowntrans)); // assert_always(downtrans->targetlevelindex == lowerlevel); downtrans->lineindex = lineindex; @@ -1052,13 +1044,13 @@ static void read_atomicdata_files() { const int nloweruptrans = get_nuptrans(element, ion, lowerlevel); auto *uptranslist = globals::elements[element].ions[ion].levels[lowerlevel].uptrans; auto *uptrans = std::find_if(uptranslist, uptranslist + nloweruptrans, - [=](auto &uptrans) { return uptrans.targetlevelindex == upperlevel; }); + [=](const auto &uptrans) { return uptrans.targetlevelindex == upperlevel; }); assert_always(uptrans != (uptranslist + nloweruptrans)); // assert_always(uptrans->targetlevelindex == upperlevel); uptrans->lineindex = lineindex; } - printout("took %ds\n", time(nullptr) - time_start_establish_linelist_connections); + printout(" took %ds\n", time(nullptr) - time_start_establish_linelist_connections); for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); @@ -1144,7 +1136,7 @@ static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcont index = -1; *index_in_groundlevelcontestimator = -1; } else { - int i = 1; + int i; int element = -1; int ion = -1; for (i = 1; i < globals::nbfcontinua_ground; i++) { @@ -1155,7 +1147,7 @@ static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcont if (i == globals::nbfcontinua_ground) { element = globals::groundcont[i - 1].element; ion = globals::groundcont[i - 1].ion; - int const level = globals::groundcont[i - 1].level; + const int level = globals::groundcont[i - 1].level; if (element == el && ion == in && level == ll) { index = i - 1; } else { @@ -1403,8 +1395,8 @@ static void setup_phixs_list() { } if (globals::nbfcontinua > 0) { - globals::phixslist[itid].kappa_bf_sum = static_cast(malloc(globals::nbfcontinua * sizeof(double))); - assert_always(globals::phixslist[itid].kappa_bf_sum != nullptr); + globals::phixslist[itid].chi_bf_sum = static_cast(malloc(globals::nbfcontinua * sizeof(double))); + assert_always(globals::phixslist[itid].chi_bf_sum != nullptr); if constexpr (DETAILED_BF_ESTIMATORS_ON) { globals::phixslist[itid].gamma_contr = static_cast(malloc(globals::nbfcontinua * sizeof(double))); @@ -1412,18 +1404,18 @@ static void setup_phixs_list() { } for (int allcontindex = 0; allcontindex < globals::nbfcontinua; allcontindex++) { - globals::phixslist[itid].kappa_bf_sum[allcontindex] = 0.; + globals::phixslist[itid].chi_bf_sum[allcontindex] = 0.; if constexpr (DETAILED_BF_ESTIMATORS_ON) { globals::phixslist[itid].gamma_contr[allcontindex] = 0.; } } } else { - globals::phixslist[itid].kappa_bf_sum = nullptr; + globals::phixslist[itid].chi_bf_sum = nullptr; globals::phixslist[itid].gamma_contr = nullptr; } - printout("[info] mem_usage: phixslist[tid].kappa_bf_contr for thread %d occupies %.3f MB\n", itid, + printout("[info] mem_usage: phixslist[tid].chi_bf_contr for thread %d occupies %.3f MB\n", itid, globals::nbfcontinua * sizeof(double) / 1024. / 1024.); } @@ -1571,62 +1563,7 @@ static void setup_phixs_list() { globals::allcont = nonconstallcont; nonconstallcont = nullptr; - size_t mem_usage_photoionluts = 2 * TABLESIZE * globals::nbfcontinua * sizeof(double); - - if (globals::nbfcontinua > 0) { -#ifdef MPI_ON - MPI_Win win = MPI_WIN_NULL; - MPI_Aint size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; - int disp_unit = sizeof(double); - assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, - &globals::spontrecombcoeff, &win) == MPI_SUCCESS); - assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &globals::spontrecombcoeff) == MPI_SUCCESS); -#else - globals::spontrecombcoeff = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); -#endif - assert_always(globals::spontrecombcoeff != nullptr); - - if constexpr (USE_LUT_PHOTOION) { -#ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; - assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, - &globals::corrphotoioncoeff, &win) == MPI_SUCCESS); - assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &globals::corrphotoioncoeff) == MPI_SUCCESS); -#else - globals::corrphotoioncoeff = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); -#endif - assert_always(globals::corrphotoioncoeff != nullptr); - mem_usage_photoionluts += TABLESIZE * globals::nbfcontinua * sizeof(double); - } - - if constexpr (USE_LUT_BFHEATING) { -#ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; - assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, - &globals::bfheating_coeff, &win) == MPI_SUCCESS); - assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &globals::bfheating_coeff) == MPI_SUCCESS); -#else - globals::bfheating_coeff = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); -#endif - assert_always(globals::bfheating_coeff != nullptr); - mem_usage_photoionluts += TABLESIZE * globals::nbfcontinua * sizeof(double); - } - -#ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; - assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, - &globals::bfcooling_coeff, &win) == MPI_SUCCESS); - assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &globals::bfcooling_coeff) == MPI_SUCCESS); -#else - globals::bfcooling_coeff = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); -#endif - assert_always(globals::bfcooling_coeff != nullptr); - } - - printout( - "[info] mem_usage: lookup tables derived from photoionisation (spontrecombcoeff, bfcooling and " - "corrphotoioncoeff/bfheating if enabled) occupy %.3f MB\n", - mem_usage_photoionluts / 1024. / 1024.); + setup_photoion_luts(); } static void read_atomicdata() { @@ -1649,6 +1586,8 @@ static void read_atomicdata() { /// Printout some information about the read-in model atom + update_includedions_maxnions(); + int includedlevels = 0; int includedionisinglevels = 0; int includedboundboundtransitions = 0; @@ -1694,8 +1633,12 @@ static void read_atomicdata() { globals::total_nlte_levels = 0; int n_super_levels = 0; - if (NLTE_POPS_ON) { - for (int element = 0; element < get_nelements(); element++) { + for (int element = 0; element < get_nelements(); element++) { + globals::elements[element].has_nlte_levels = elem_has_nlte_levels_search(element); + } + + for (int element = 0; element < get_nelements(); element++) { + if (elem_has_nlte_levels(element)) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { globals::elements[element].ions[ion].first_nlte = globals::total_nlte_levels; @@ -1707,6 +1650,7 @@ static void read_atomicdata() { globals::total_nlte_levels++; } } + globals::elements[element].ions[ion].nlevels_nlte = fullnlteexcitedlevelcount; const bool has_superlevel = (nlevels > (fullnlteexcitedlevelcount + 1)); if (has_superlevel) { @@ -1717,8 +1661,6 @@ static void read_atomicdata() { n_super_levels++; } - globals::elements[element].ions[ion].nlevels_nlte = fullnlteexcitedlevelcount; - assert_always(has_superlevel == ion_has_superlevel(element, ion)); printout("[input] element %2d Z=%2d ion_stage %2d has %5d NLTE excited levels%s. Starting at %d\n", element, @@ -1734,10 +1676,8 @@ static void read_atomicdata() { void input(int rank) /// To govern the input. For now hardwire everything. { - globals::homogeneous_abundances = false; - globals::n_titer = 1; - globals::initial_iteration = false; + globals::lte_iteration = false; printout("[info] input: do n_titer %d iterations per timestep\n", globals::n_titer); if (globals::n_titer > 1) { @@ -1775,17 +1715,11 @@ void input(int rank) grid::read_ejecta_model(); } -static auto getline(std::istream &input, std::string &line) -> bool -// return true if line read, false if not (EOF) -{ - return !(!std::getline(input, line)); -} - -auto get_noncommentline(std::istream &input, std::string &line) -> bool +auto get_noncommentline(std::fstream &input, std::string &line) -> bool // read the next line, skipping any comment lines beginning with '#' { while (true) { - bool const linefound = getline(input, line); + const bool linefound = !(!std::getline(input, line)); // printout("LINE: >%s< linefound: %s commentonly: %s \n", line.c_str(), linefound ? "true" : "false", // lineiscommentonly(line) ? "true" : "false"); if (!linefound) { @@ -1800,27 +1734,22 @@ auto get_noncommentline(std::istream &input, std::string &line) -> bool void read_parameterfile(int rank) /// Subroutine to read in input parameters from input.txt. { - unsigned long pre_zseed = 0; + uint_least64_t pre_zseed = 0; - std::ifstream file("input.txt"); - assert_always(file.is_open()); + auto file = fstream_required("input.txt", std::ios::in); std::string line; assert_always(get_noncommentline(file, line)); - long zseed_input = 0; - std::stringstream(line) >> zseed_input; + uint_fast64_t zseed_input = 0; + std::istringstream(line) >> zseed_input; if (zseed_input > 0) { pre_zseed = zseed_input; // random number seed printout("using input.txt specified random number seed of %lu\n", pre_zseed); } else { - if constexpr (USE_GSL_RANDOM) { - pre_zseed = time(nullptr); - } else { - pre_zseed = std::random_device{}(); - } + pre_zseed = std::random_device{}(); printout("randomly-generated random number seed is %lu\n", pre_zseed); } @@ -1831,11 +1760,10 @@ void read_parameterfile(int rank) /// For MPI parallelisation, the random seed is changed based on the rank of the process /// For OpenMP parallelisation rng is a threadprivate variable and the seed changed according /// to the thread-ID tid. - const uint_fast64_t zseed = pre_zseed + (13 * rank) + (17 * tid); /* rnum generator seed */ + const auto zseed = pre_zseed + 13 * (rank * get_num_threads() + tid); printout("rank %d: thread %d has zseed %lu\n", rank, tid, zseed); - /// start by setting up the randon number generator rng_init(zseed); - /// call it a few times to get it in motion. + /// call it a few times for (int n = 0; n < 100; n++) { rng_uniform(); } @@ -1844,21 +1772,21 @@ void read_parameterfile(int rank) #endif assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::ntstep; // number of time steps - assert_always(globals::ntstep > 0); + std::istringstream(line) >> globals::ntimesteps; // number of time steps + assert_always(globals::ntimesteps > 0); assert_always(get_noncommentline(file, line)); - // printout("line %s\n", line.c_str()); - std::stringstream(line) >> globals::itstep >> globals::ftstep; // number of start and end time step - printout("input: itstep %d ftstep %d\n", globals::itstep, globals::ftstep); - assert_always(globals::itstep < globals::ntstep); - assert_always(globals::itstep <= globals::ftstep); - assert_always(globals::ftstep <= globals::ntstep); + std::istringstream(line) >> globals::timestep_initial >> + globals::timestep_finish; // number of start and end time step + printout("input: timestep_start %d timestep_finish %d\n", globals::timestep_initial, globals::timestep_finish); + assert_always(globals::timestep_initial < globals::ntimesteps); + assert_always(globals::timestep_initial <= globals::timestep_finish); + assert_always(globals::timestep_finish <= globals::ntimesteps); double tmin_days = 0.; double tmax_days = 0.; assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> tmin_days >> tmax_days; // start and end times + std::istringstream(line) >> tmin_days >> tmax_days; // start and end times assert_always(tmin_days > 0); assert_always(tmax_days > 0); assert_always(tmin_days < tmax_days); @@ -1873,13 +1801,13 @@ void read_parameterfile(int rank) assert_always(get_noncommentline(file, line)); // model dimensions int dum1 = 0; - std::stringstream(line) >> dum1; + std::istringstream(line) >> dum1; if (dum1 == 1) { - set_model_type(grid::RHO_1D_READ); + grid::set_model_type(GRID_SPHERICAL1D); } else if (dum1 == 2) { - set_model_type(grid::RHO_2D_READ); + grid::set_model_type(GRID_CYLINDRICAL2D); } else if (dum1 == 3) { - set_model_type(grid::RHO_3D_READ); + grid::set_model_type(GRID_CARTESIAN3D); } assert_always(get_noncommentline(file, line)); // UNUSED compute the r-light curve? @@ -1889,11 +1817,11 @@ void read_parameterfile(int rank) assert_always(get_noncommentline(file, line)); // UNUSED change speed of light assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::gamma_grey; // use grey opacity for gammas? + std::istringstream(line) >> globals::gamma_kappagrey; // use grey opacity for gammas? float syn_dir_in[3]; assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> syn_dir_in[0] >> syn_dir_in[1] >> syn_dir_in[2]; // components of syn_dir + std::istringstream(line) >> syn_dir_in[0] >> syn_dir_in[1] >> syn_dir_in[2]; // components of syn_dir const double rr = (syn_dir_in[0] * syn_dir_in[0]) + (syn_dir_in[1] * syn_dir_in[1]) + (syn_dir_in[2] * syn_dir_in[2]); // ensure that this vector is normalised. @@ -1910,42 +1838,42 @@ void read_parameterfile(int rank) } assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::opacity_case; // opacity choice + std::istringstream(line) >> globals::opacity_case; // opacity choice assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::rho_crit_para; // free parameter for calculation of rho_crit + std::istringstream(line) >> globals::rho_crit_para; // free parameter for calculation of rho_crit printout("input: rho_crit_para %g\n", globals::rho_crit_para); /// he calculation of rho_crit itself depends on the time, therfore it happens in grid_init and update_grid assert_always(get_noncommentline(file, line)); int debug_packet = 0; - std::stringstream(line) >> debug_packet; // activate debug output for packet + std::istringstream(line) >> debug_packet; // activate debug output for packet assert_always(debug_packet == -1); // select a negative value to deactivate // Do we start a new simulation or, continue another one? int continue_flag = 0; assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> continue_flag; + std::istringstream(line) >> continue_flag; globals::simulation_continued_from_saved = (continue_flag == 1); if (globals::simulation_continued_from_saved) { printout("input: resuming simulation from saved point\n"); } else { printout("input: starting a new simulation\n"); - assert_always(globals::itstep == 0); + assert_always(globals::timestep_initial == 0); } /// Wavelength (in Angstroms) at which the parameterisation of the radiation field /// switches from the nebular approximation to LTE. float dum2 = NAN; assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> dum2; // free parameter for calculation of rho_crit + std::istringstream(line) >> dum2; // free parameter for calculation of rho_crit globals::nu_rfcut = CLIGHT / (dum2 * 1e-8); printout("input: nu_rfcut %g\n", globals::nu_rfcut); /// Sets the number of initial LTE timesteps for NLTE runs assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::num_lte_timesteps; + std::istringstream(line) >> globals::num_lte_timesteps; printout("input: doing the first %d timesteps in LTE\n", globals::num_lte_timesteps); if (NT_ON) { @@ -1958,27 +1886,27 @@ void read_parameterfile(int rank) printout("input: No non-thermal ionisation is used in this run.\n"); } - if (!USE_LUT_PHOTOION) { - printout( - "Corrphotoioncoeff is calculated from the radiation field at each timestep in each modelgrid cell (no " - "LUT).\n"); - } else { + if (USE_LUT_PHOTOION) { printout( "Corrphotoioncoeff is calculated from LTE lookup tables (ratecoeff.dat) and corrphotoionrenorm " "estimator.\n"); + } else { + printout( + "Corrphotoioncoeff is calculated from the radiation field at each timestep in each modelgrid cell (no " + "LUT).\n"); } - if (!USE_LUT_BFHEATING) { + if (USE_LUT_BFHEATING) { + printout("bfheating coefficients are calculated from LTE lookup tables (ratecoeff.dat) and bfheatingestimator.\n"); + } else { printout( "bfheating coefficients are calculated from the radiation field at each timestep in each modelgrid cell (no " "LUT).\n"); - } else { - printout("bfheating coefficients are calculated from LTE lookup tables (ratecoeff.dat) and bfheatingestimator.\n"); } /// Set up initial grey approximation? assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::cell_is_optically_thick >> globals::num_grey_timesteps; + std::istringstream(line) >> globals::cell_is_optically_thick >> globals::num_grey_timesteps; printout( "input: cells with Thomson optical depth > %g are treated in grey approximation for the first %d timesteps\n", globals::cell_is_optically_thick, globals::num_grey_timesteps); @@ -1986,16 +1914,16 @@ void read_parameterfile(int rank) /// Limit the number of bf-continua assert_always(get_noncommentline(file, line)); int max_bf_continua = 0; - std::stringstream(line) >> max_bf_continua; + std::istringstream(line) >> max_bf_continua; assert_always(max_bf_continua == -1); /// for exspec: read number of MPI tasks assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::nprocs_exspec; + std::istringstream(line) >> globals::nprocs_exspec; /// Extract line-of-sight dependent information of last emission for spectrum_res assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> dum1; + std::istringstream(line) >> dum1; globals::do_emission_res = (dum1 != 0); /// To reduce the work imbalance between different MPI tasks I introduced a diffusion @@ -2006,7 +1934,7 @@ void read_parameterfile(int rank) /// kpkts live. Parameter two (an int) gives the number of time steps for which we /// want to use this approximation assert_always(get_noncommentline(file, line)); - std::stringstream(line) >> globals::kpktdiffusion_timescale >> globals::n_kpktdiffusion_timesteps; + std::istringstream(line) >> globals::kpktdiffusion_timescale >> globals::n_kpktdiffusion_timesteps; printout("input: kpkts diffuse %g of a time step's length for the first %d time steps\n", globals::kpktdiffusion_timescale, globals::n_kpktdiffusion_timesteps); @@ -2054,7 +1982,7 @@ void update_parameterfile(int nts) if (nts >= 0) { if (noncomment_linenum == 2) { /// Number of start and end time step - snprintf(c_line, 1024, "%d %d", nts, globals::ftstep); + snprintf(c_line, 1024, "%d %d", nts, globals::timestep_finish); // line.assign(c_line); line.replace(line.begin(), line.end(), c_line); } else if (noncomment_linenum == 16) { @@ -2102,32 +2030,32 @@ void update_parameterfile(int nts) } void time_init() -// Subroutine to define the time steps. +// define the time steps { - /// t=globals::tmin is the start of the calcualtion. t=globals::tmax is the end of the calculation. - /// globals::ntstep is the number of time steps wanted. + /// t=globals::tmin is the start of the calculation. t=globals::tmax is the end of the calculation. + /// globals::ntimesteps is the number of time steps - globals::time_step = static_cast(malloc((globals::ntstep + 1) * sizeof(struct time))); + globals::timesteps = std::make_unique(globals::ntimesteps + 1); /// Now set the individual time steps switch (TIMESTEP_SIZE_METHOD) { case TIMESTEP_SIZES_LOGARITHMIC: { - for (int n = 0; n < globals::ntstep; n++) { // For logarithmic steps, the logarithmic inverval will be - const double dlogt = (log(globals::tmax) - log(globals::tmin)) / globals::ntstep; - globals::time_step[n].start = globals::tmin * exp(n * dlogt); - globals::time_step[n].mid = globals::tmin * exp((n + 0.5) * dlogt); - globals::time_step[n].width = (globals::tmin * exp((n + 1) * dlogt)) - globals::time_step[n].start; + for (int n = 0; n < globals::ntimesteps; n++) { // For logarithmic steps, the logarithmic inverval will be + const double dlogt = (log(globals::tmax) - log(globals::tmin)) / globals::ntimesteps; + globals::timesteps[n].start = globals::tmin * exp(n * dlogt); + globals::timesteps[n].mid = globals::tmin * exp((n + 0.5) * dlogt); + globals::timesteps[n].width = (globals::tmin * exp((n + 1) * dlogt)) - globals::timesteps[n].start; } break; } case TIMESTEP_SIZES_CONSTANT: { - for (int n = 0; n < globals::ntstep; n++) { + for (int n = 0; n < globals::ntimesteps; n++) { // for constant timesteps - const double dt = (globals::tmax - globals::tmin) / globals::ntstep; - globals::time_step[n].start = globals::tmin + n * dt; - globals::time_step[n].width = dt; - globals::time_step[n].mid = globals::time_step[n].start + 0.5 * globals::time_step[n].width; + const double dt = (globals::tmax - globals::tmin) / globals::ntimesteps; + globals::timesteps[n].start = globals::tmin + n * dt; + globals::timesteps[n].width = dt; + globals::timesteps[n].mid = globals::timesteps[n].start + 0.5 * globals::timesteps[n].width; } break; } @@ -2141,25 +2069,25 @@ void time_init() const int nts_fixed = ceil((globals::tmax - t_transition) / maxtsdelta); const double fixed_tsdelta = (globals::tmax - t_transition) / nts_fixed; assert_always(nts_fixed > 0); - assert_always(nts_fixed < globals::ntstep); - const int nts_log = globals::ntstep - nts_fixed; + assert_always(nts_fixed < globals::ntimesteps); + const int nts_log = globals::ntimesteps - nts_fixed; assert_always(nts_log > 0); - assert_always(nts_log < globals::ntstep); - assert_always((nts_log + nts_fixed) == globals::ntstep); - for (int n = 0; n < globals::ntstep; n++) { + assert_always(nts_log < globals::ntimesteps); + assert_always((nts_log + nts_fixed) == globals::ntimesteps); + for (int n = 0; n < globals::ntimesteps; n++) { if (n < nts_log) { // For logarithmic steps, the logarithmic inverval will be const double dlogt = (log(t_transition) - log(globals::tmin)) / nts_log; - globals::time_step[n].start = globals::tmin * exp(n * dlogt); - globals::time_step[n].mid = globals::tmin * exp((n + 0.5) * dlogt); - globals::time_step[n].width = (globals::tmin * exp((n + 1) * dlogt)) - globals::time_step[n].start; + globals::timesteps[n].start = globals::tmin * exp(n * dlogt); + globals::timesteps[n].mid = globals::tmin * exp((n + 0.5) * dlogt); + globals::timesteps[n].width = (globals::tmin * exp((n + 1) * dlogt)) - globals::timesteps[n].start; } else { // for constant timesteps const double prev_start = - n > 0 ? (globals::time_step[n - 1].start + globals::time_step[n - 1].width) : globals::tmin; - globals::time_step[n].start = prev_start; - globals::time_step[n].width = fixed_tsdelta; - globals::time_step[n].mid = globals::time_step[n].start + 0.5 * globals::time_step[n].width; + n > 0 ? (globals::timesteps[n - 1].start + globals::timesteps[n - 1].width) : globals::tmin; + globals::timesteps[n].start = prev_start; + globals::timesteps[n].width = fixed_tsdelta; + globals::timesteps[n].mid = globals::timesteps[n].start + 0.5 * globals::timesteps[n].width; } } break; @@ -2174,25 +2102,25 @@ void time_init() const int nts_fixed = ceil((t_transition - globals::tmin) / maxtsdelta); const double fixed_tsdelta = (t_transition - globals::tmin) / nts_fixed; assert_always(nts_fixed > 0); - assert_always(nts_fixed < globals::ntstep); - const int nts_log = globals::ntstep - nts_fixed; + assert_always(nts_fixed < globals::ntimesteps); + const int nts_log = globals::ntimesteps - nts_fixed; assert_always(nts_log > 0); - assert_always(nts_log < globals::ntstep); - assert_always((nts_log + nts_fixed) == globals::ntstep); - for (int n = 0; n < globals::ntstep; n++) { + assert_always(nts_log < globals::ntimesteps); + assert_always((nts_log + nts_fixed) == globals::ntimesteps); + for (int n = 0; n < globals::ntimesteps; n++) { if (n < nts_fixed) { // for constant timesteps - globals::time_step[n].start = globals::tmin + n * fixed_tsdelta; - globals::time_step[n].width = fixed_tsdelta; - globals::time_step[n].mid = globals::time_step[n].start + 0.5 * globals::time_step[n].width; + globals::timesteps[n].start = globals::tmin + n * fixed_tsdelta; + globals::timesteps[n].width = fixed_tsdelta; + globals::timesteps[n].mid = globals::timesteps[n].start + 0.5 * globals::timesteps[n].width; } else { // For logarithmic time steps, the logarithmic interval will be const double dlogt = (log(globals::tmax) - log(t_transition)) / nts_log; const double prev_start = - n > 0 ? (globals::time_step[n - 1].start + globals::time_step[n - 1].width) : globals::tmin; - globals::time_step[n].start = prev_start; - globals::time_step[n].width = (t_transition * exp((n - nts_fixed + 1) * dlogt)) - globals::time_step[n].start; - globals::time_step[n].mid = globals::time_step[n].start + 0.5 * globals::time_step[n].width; + n > 0 ? (globals::timesteps[n - 1].start + globals::timesteps[n - 1].width) : globals::tmin; + globals::timesteps[n].start = prev_start; + globals::timesteps[n].width = (t_transition * exp((n - nts_fixed + 1) * dlogt)) - globals::timesteps[n].start; + globals::timesteps[n].mid = globals::timesteps[n].start + 0.5 * globals::timesteps[n].width; } } break; @@ -2204,65 +2132,66 @@ void time_init() // to limit the timestep durations // const double maxt = 0.5 * DAY; - // for (int n = globals::ntstep - 1; n > 0; n--) + // for (int n = globals::ntimesteps - 1; n > 0; n--) // { - // if (globals::time_step[n].width > maxt) + // if (globals::timesteps[n].width > maxt) // { - // const double boundaryshift = globals::time_step[n].width - maxt; - // globals::time_step[n].width -= boundaryshift; - // globals::time_step[n].start += boundaryshift; - // globals::time_step[n - 1].width += boundaryshift; + // const double boundaryshift = globals::timesteps[n].width - maxt; + // globals::timesteps[n].width -= boundaryshift; + // globals::timesteps[n].start += boundaryshift; + // globals::timesteps[n - 1].width += boundaryshift; // } - // else if (n < globals::ntstep - 1 && globals::time_step[n + 1].width > maxt) + // else if (n < globals::ntimesteps - 1 && globals::timesteps[n + 1].width > maxt) // { // printout("TIME: Keeping logarithmic durations for timesteps <= %d\n", n); // } // } - // assert_always(globals::time_step[0].width <= maxt); // no solution is possible with these constraints! + // assert_always(globals::timesteps[0].width <= maxt); // no solution is possible with these constraints! /// and add a dummy timestep which contains the endtime /// of the calculation - globals::time_step[globals::ntstep].start = globals::tmax; - globals::time_step[globals::ntstep].mid = globals::tmax; - globals::time_step[globals::ntstep].width = 0.; + globals::timesteps[globals::ntimesteps].start = globals::tmax; + globals::timesteps[globals::ntimesteps].mid = globals::tmax; + globals::timesteps[globals::ntimesteps].width = 0.; // check consistency of start + width = start_next - for (int n = 1; n < globals::ntstep; n++) { + for (int n = 1; n < globals::ntimesteps; n++) { assert_always( - fabs((globals::time_step[n - 1].start + globals::time_step[n - 1].width) / globals::time_step[n].start) - 1 < + fabs((globals::timesteps[n - 1].start + globals::timesteps[n - 1].width) / globals::timesteps[n].start) - 1 < 0.001); } - assert_always(fabs((globals::time_step[globals::ntstep - 1].start + globals::time_step[globals::ntstep - 1].width) / - globals::tmax) - - 1 < - 0.001); - - for (int n = 0; n < globals::ntstep; n++) { - globals::time_step[n].positron_dep = 0.; - globals::time_step[n].eps_positron_ana_power = 0.; - globals::time_step[n].electron_dep = 0.; - globals::time_step[n].electron_emission = 0.; - globals::time_step[n].eps_electron_ana_power = 0.; - globals::time_step[n].alpha_dep = 0.; - globals::time_step[n].alpha_emission = 0.; - globals::time_step[n].eps_alpha_ana_power = 0.; - globals::time_step[n].gamma_dep = 0.; - globals::time_step[n].gamma_dep_pathint = 0.; - globals::time_step[n].qdot_betaminus = 0.; - globals::time_step[n].qdot_alpha = 0.; - globals::time_step[n].qdot_total = 0.; - globals::time_step[n].gamma_emission = 0.; - globals::time_step[n].cmf_lum = 0.0; - globals::time_step[n].pellet_decays = 0; + assert_always( + fabs((globals::timesteps[globals::ntimesteps - 1].start + globals::timesteps[globals::ntimesteps - 1].width) / + globals::tmax) - + 1 < + 0.001); + + for (int n = 0; n < globals::ntimesteps; n++) { + globals::timesteps[n].positron_dep = 0.; + globals::timesteps[n].eps_positron_ana_power = 0.; + globals::timesteps[n].electron_dep = 0.; + globals::timesteps[n].electron_emission = 0.; + globals::timesteps[n].eps_electron_ana_power = 0.; + globals::timesteps[n].alpha_dep = 0.; + globals::timesteps[n].alpha_emission = 0.; + globals::timesteps[n].eps_alpha_ana_power = 0.; + globals::timesteps[n].gamma_dep = 0.; + globals::timesteps[n].gamma_dep_pathint = 0.; + globals::timesteps[n].qdot_betaminus = 0.; + globals::timesteps[n].qdot_alpha = 0.; + globals::timesteps[n].qdot_total = 0.; + globals::timesteps[n].gamma_emission = 0.; + globals::timesteps[n].cmf_lum = 0.0; + globals::timesteps[n].pellet_decays = 0; } } void write_timestep_file() { FILE *timestepfile = fopen_required("timesteps.out", "w"); fprintf(timestepfile, "#timestep tstart_days tmid_days twidth_days\n"); - for (int n = 0; n < globals::ntstep; n++) { - fprintf(timestepfile, "%d %lg %lg %lg\n", n, globals::time_step[n].start / DAY, globals::time_step[n].mid / DAY, - globals::time_step[n].width / DAY); + for (int n = 0; n < globals::ntimesteps; n++) { + fprintf(timestepfile, "%d %lg %lg %lg\n", n, globals::timesteps[n].start / DAY, globals::timesteps[n].mid / DAY, + globals::timesteps[n].width / DAY); } fclose(timestepfile); -} +} \ No newline at end of file diff --git a/input.h b/input.h index f8d102b2c..49f63025e 100644 --- a/input.h +++ b/input.h @@ -10,17 +10,15 @@ void read_parameterfile(int rank); void update_parameterfile(int nts); void time_init(); void write_timestep_file(); -bool get_noncommentline(std::istream &input, std::string &line); +bool get_noncommentline(std::fstream &input, std::string &line); static inline bool lineiscommentonly(const std::string &line) // return true for whitepace-only lines, and lines that are exclusively whitepace up to a '#' character { - int searchlength = line.find('#'); // ignore anything to the right of a # character - if (searchlength < 0) { - searchlength = line.length(); - } - - for (int i = 0; i < searchlength; i++) { + for (size_t i = 0; i < line.length(); i++) { + if (line[i] == '#') { // anything to the right of a # character doesn't count + return true; + } if (line[i] != ' ') { return false; } diff --git a/kpkt.cc b/kpkt.cc index dc67f7847..f396d4d12 100644 --- a/kpkt.cc +++ b/kpkt.cc @@ -36,142 +36,18 @@ struct cellhistorycoolinglist { static struct cellhistorycoolinglist *coolinglist; -static auto get_ncoolingterms(int element, int ion) -> int { +static auto get_ncoolingterms_ion(int element, int ion) -> int { return globals::elements[element].ions[ion].ncoolingterms; } -static auto get_bfcoolingcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double { - const int lowerindex = floor(log(T_e / MINTEMP) / T_step_log); - if (lowerindex < TABLESIZE - 1) { - const int upperindex = lowerindex + 1; - const double T_lower = MINTEMP * exp(lowerindex * T_step_log); - const double T_upper = MINTEMP * exp(upperindex * T_step_log); - - const double f_upper = globals::bfcooling_coeff[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; - const double f_lower = globals::bfcooling_coeff[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; - - return (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T_e - T_lower)); - } - return globals::bfcooling_coeff[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; -} - -void calculate_cooling_rates(const int modelgridindex, struct heatingcoolingrates *heatingcoolingrates) -// Calculate the cooling rates for a given cell and store them for each ion -// optionally store components (ff, bf, collisional) in heatingcoolingrates struct -{ - const auto nne = grid::get_nne(modelgridindex); - const auto T_e = grid::get_Te(modelgridindex); - - double C_ff_all = 0.; /// free-free creation of rpkts - double C_fb_all = 0.; /// free-bound creation of rpkt - double C_exc_all = 0.; /// collisional excitation of macroatoms - double C_ionization_all = 0.; /// collisional ionisation of macroatoms - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions; ion++) { - double C_ion = 0.; /// all cooling for an ion - const int nionisinglevels = get_ionisinglevels(element, ion); - const double nncurrention = ionstagepop(modelgridindex, element, ion); - - /// ff creation of rpkt - const int ioncharge = get_ionstage(element, ion) - 1; - if (ioncharge > 0) { - const double C_ff_ion = 1.426e-27 * sqrt(T_e) * pow(ioncharge, 2) * nncurrention * nne; - C_ff_all += C_ff_ion; - C_ion += C_ff_ion; - } - - const int nlevels = get_nlevels(element, ion); - - /// excitation to same ionization stage - /// ----------------------------------- - for (int level = 0; level < nlevels; level++) { - const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - const double epsilon_current = epsilon(element, ion, level); - const double statweight = stat_weight(element, ion, level); - - const int nuptrans = get_nuptrans(element, ion, level); - for (int ii = 0; ii < nuptrans; ii++) { - const int upper = globals::elements[element].ions[ion].levels[level].uptrans[ii].targetlevelindex; - // printout(" excitation to level %d possible\n",upper); - const double epsilon_trans = epsilon(element, ion, upper) - epsilon_current; - const double C = nnlevel * - col_excitation_ratecoeff(T_e, nne, element, ion, level, ii, epsilon_trans, statweight) * - epsilon_trans; - C_exc_all += C; - C_ion += C; - } - } - - if (ion < nions - 1) { - const double nnupperion = ionstagepop(modelgridindex, element, ion + 1); - - for (int level = 0; level < nionisinglevels; level++) { - // printout("[debug] do_kpkt: element %d, ion %d, level %d\n", element, ion, level); - const double epsilon_current = epsilon(element, ion, level); - const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - - /// ionization to higher ionization stage - const int nphixstargets = get_nphixstargets(element, ion, level); - for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { - const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); - const double epsilon_upper = epsilon(element, ion + 1, upper); - const double epsilon_trans = epsilon_upper - epsilon_current; - // printout("cooling list: col_ionization\n"); - const double C_ionization_ion_thistarget = - nnlevel * col_ionization_ratecoeff(T_e, nne, element, ion, level, phixstargetindex, epsilon_trans) * - epsilon_trans; - C_ionization_all += C_ionization_ion_thistarget; - C_ion += C_ionization_ion_thistarget; - } - } - - /// fb creation of r-pkt - /// free bound rates are calculated from the lower ion, but associated to the higher ion - for (int level = 0; level < nionisinglevels; level++) { - const int nphixstargets = get_nphixstargets(element, ion, level); - for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { - const double pop = (BFCOOLING_USELEVELPOPNOTIONPOP - ? get_levelpop(modelgridindex, element, ion + 1, - get_phixsupperlevel(element, ion, level, phixstargetindex)) - : nnupperion); - - const double C_fb_ion_thistarget = - get_bfcoolingcoeff(element, ion, level, phixstargetindex, T_e) * pop * nne; - C_fb_all += C_fb_ion_thistarget; - C_ion += C_fb_ion_thistarget; - } - } - } - - grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion] = C_ion; - } - } - - // this loop is made separate for future parallelisation of upper loop. - // the ion contributions must be added in this exact order - double C_total = 0.; - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions; ion++) { - C_total += grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]; - } - } - grid::modelgrid[modelgridindex].totalcooling = C_total; - - // only used in the T_e solver and write_to_estimators file - if (heatingcoolingrates != nullptr) { - heatingcoolingrates->cooling_collisional = C_exc_all + C_ionization_all; - heatingcoolingrates->cooling_fb = C_fb_all; - heatingcoolingrates->cooling_ff = C_ff_all; - } -} - -static void calculate_kpkt_rates_ion(int modelgridindex, int element, int ion, int indexionstart, int tid) +template +static auto calculate_cooling_rates_ion(const int modelgridindex, const int element, const int ion, + const int indexionstart, const int tid, double *C_ff, double *C_fb, + double *C_exc, double *C_ionization) -> double // calculate the cooling contribution list of individual levels/processes for an ion // oldcoolingsum is the sum of lower ion (of same element or all ions of lower elements) cooling contributions { - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); const auto T_e = grid::get_Te(modelgridindex); double C_ion = 0.; @@ -180,24 +56,29 @@ static void calculate_kpkt_rates_ion(int modelgridindex, int element, int ion, i const int nions = get_nions(element); const int nionisinglevels = get_ionisinglevels(element, ion); - const double nncurrention = ionstagepop(modelgridindex, element, ion); + const double nncurrention = get_nnion(modelgridindex, element, ion); /// ff creation of rpkt const int ioncharge = get_ionstage(element, ion) - 1; // printout("[debug] ioncharge %d, nncurrention %g, nne %g\n",ion,nncurrention,nne); if (ioncharge > 0) { - const double C = 1.426e-27 * sqrt(T_e) * pow(ioncharge, 2) * nncurrention * nne; - C_ion += C; - globals::cellhistory[tid].cooling_contrib[i] = C_ion; - - assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_FF); - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); - - // if (contrib < oldcoolingsum) printout("contrib %g < oldcoolingsum %g: C%g, element %d, ion %d, level %d, - // coolingtype %d, i %d, low - // %d\n",contrib,oldcoolingsum,C,element,ion,-99,globals::cellhistory[tid].coolinglist[i].type,i,low); - i++; + const double C_ff_ion = 1.426e-27 * sqrt(T_e) * pow(ioncharge, 2) * nncurrention * nne; + C_ion += C_ff_ion; + + if constexpr (update_cooling_contrib_list) { + globals::cellhistory[tid].cooling_contrib[i] = C_ion; + + assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_FF); + assert_testmodeonly(coolinglist[i].element == element); + assert_testmodeonly(coolinglist[i].ion == ion); + + // if (contrib < oldcoolingsum) printout("contrib %g < oldcoolingsum %g: C%g, element %d, ion %d, level %d, + // coolingtype %d, i %d, low + // %d\n",contrib,oldcoolingsum,C,element,ion,-99,globals::cellhistory[tid].coolinglist[i].type,i,low); + i++; + } else { + *C_ff += C_ff_ion; + } } /// excitation to same ionization stage @@ -217,19 +98,24 @@ static void calculate_kpkt_rates_ion(int modelgridindex, int element, int ion, i col_excitation_ratecoeff(T_e, nne, element, ion, level, ii, epsilon_trans, statweight) * epsilon_trans; C_ion += C; + if constexpr (!update_cooling_contrib_list) { + *C_exc += C; + } } - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + if constexpr (update_cooling_contrib_list) { + globals::cellhistory[tid].cooling_contrib[i] = C_ion; - assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_COLLEXC); - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); + assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_COLLEXC); + assert_testmodeonly(coolinglist[i].element == element); + assert_testmodeonly(coolinglist[i].ion == ion); - i++; + i++; + } } } if (ion < nions - 1) { - const double nnupperion = ionstagepop(modelgridindex, element, ion + 1); + const double nnupperion = get_nnion(modelgridindex, element, ion + 1); // ionization to higher ionization stage for (int level = 0; level < nionisinglevels; level++) { @@ -245,15 +131,19 @@ static void calculate_kpkt_rates_ion(int modelgridindex, int element, int ion, i epsilon_trans; C_ion += C; - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + if constexpr (update_cooling_contrib_list) { + globals::cellhistory[tid].cooling_contrib[i] = C_ion; - assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_COLLION); - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); - assert_testmodeonly(coolinglist[i].level == level); - assert_testmodeonly(coolinglist[i].upperlevel == upper); + assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_COLLION); + assert_testmodeonly(coolinglist[i].element == element); + assert_testmodeonly(coolinglist[i].ion == ion); + assert_testmodeonly(coolinglist[i].level == level); + assert_testmodeonly(coolinglist[i].upperlevel == upper); - i++; + i++; + } else { + *C_ionization += C; + } } } @@ -269,24 +159,65 @@ static void calculate_kpkt_rates_ion(int modelgridindex, int element, int ion, i const double C = get_bfcoolingcoeff(element, ion, level, phixstargetindex, T_e) * pop * nne; C_ion += C; - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + if constexpr (update_cooling_contrib_list) { + globals::cellhistory[tid].cooling_contrib[i] = C_ion; - assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_FB); - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); - assert_testmodeonly(coolinglist[i].level == level); - assert_testmodeonly(coolinglist[i].upperlevel == get_phixsupperlevel(element, ion, level, phixstargetindex)); + assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_FB); + assert_testmodeonly(coolinglist[i].element == element); + assert_testmodeonly(coolinglist[i].ion == ion); + assert_testmodeonly(coolinglist[i].level == level); + assert_testmodeonly(coolinglist[i].upperlevel == get_phixsupperlevel(element, ion, level, phixstargetindex)); - i++; + i++; + } else { + *C_fb += C; + } } } } - assert_testmodeonly(indexionstart == get_coolinglistoffset(element, ion)); - assert_always(i == indexionstart + get_ncoolingterms(element, ion)); + if constexpr (update_cooling_contrib_list) { + assert_testmodeonly(indexionstart == get_coolinglistoffset(element, ion)); + assert_always(i == indexionstart + get_ncoolingterms_ion(element, ion)); + } + + return C_ion; +} + +void calculate_cooling_rates(const int modelgridindex, struct heatingcoolingrates *heatingcoolingrates) +// Calculate the cooling rates for a given cell and store them for each ion +// optionally store components (ff, bf, collisional) in heatingcoolingrates struct +{ + double C_ff_all = 0.; /// free-free creation of rpkts + double C_fb_all = 0.; /// free-bound creation of rpkt + double C_exc_all = 0.; /// collisional excitation of macroatoms + double C_ionization_all = 0.; /// collisional ionisation of macroatoms + for (int element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + const double C_ion = calculate_cooling_rates_ion(modelgridindex, element, ion, -1, tid, &C_ff_all, + &C_fb_all, &C_exc_all, &C_ionization_all); + grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion] = C_ion; + } + } + + // this loop is made separate for future parallelisation of upper loop. + // the ion contributions must be added in this exact order + double C_total = 0.; + for (int element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + C_total += grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]; + } + } + grid::modelgrid[modelgridindex].totalcooling = C_total; - // we just summed up every individual cooling process. make sure it matches the stored total for the ion - assert_always(grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion] == C_ion); + // only used in the T_e solver and write_to_estimators file + if (heatingcoolingrates != nullptr) { + heatingcoolingrates->cooling_collisional = C_exc_all + C_ionization_all; + heatingcoolingrates->cooling_fb = C_fb_all; + heatingcoolingrates->cooling_ff = C_ff_all; + } } static void set_ncoolingterms() { @@ -399,7 +330,7 @@ void setup_coolinglist() { } } } - assert_always(i == get_coolinglistoffset(element, ion) + get_ncoolingterms(element, ion)); + assert_always(i == get_coolinglistoffset(element, ion) + get_ncoolingterms_ion(element, ion)); } } @@ -429,19 +360,16 @@ static auto sample_planck(const double T) -> double } } -auto do_kpkt_bb(struct packet *pkt_ptr) -> double -/// Now routine to deal with a k-packet. Similar idea to do_gamma. +void do_kpkt_blackbody(struct packet *pkt_ptr) +/// handle a k-packet (e.g., in a thick cell) by emitting according to the planck function { - // double nne = globals::cell[pkt_ptr->where].nne ; - const int cellindex = pkt_ptr->where; - const int modelgridindex = grid::get_cell_modelgridindex(cellindex); - const auto T_e = grid::get_Te(modelgridindex); + const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); - pkt_ptr->nu_cmf = sample_planck(T_e); + pkt_ptr->nu_cmf = sample_planck(grid::get_Te(modelgridindex)); assert_always(std::isfinite(pkt_ptr->nu_cmf)); /// and then emitt the packet randomly in the comoving frame - emitt_rpkt(pkt_ptr); - // printout("[debug] calculate_kappa_rpkt after kpkt to rpkt by ff\n"); + emit_rpkt(pkt_ptr); + // printout("[debug] calculate_chi_rpkt after kpkt to rpkt by ff\n"); pkt_ptr->next_trans = 0; /// FLAG: transition history here not important, cont. process // if (tid == 0) k_stat_to_r_bb++; stats::increment(stats::COUNTER_K_STAT_TO_R_BB); @@ -451,17 +379,14 @@ auto do_kpkt_bb(struct packet *pkt_ptr) -> double vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->nscatterings = 0; - - return pkt_ptr->prop_time; } -auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double -/// Now routine to deal with a k-packet. Similar idea to do_gamma. +void do_kpkt(struct packet *pkt_ptr, double t2, int nts) +/// handle a k-packet (kinetic energy of the free electrons) { const int tid = get_thread_num(); const double t1 = pkt_ptr->prop_time; - const int cellindex = pkt_ptr->where; - const int modelgridindex = grid::get_cell_modelgridindex(cellindex); + const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); /// don't calculate cooling rates after each cell crossings any longer /// but only if we really get a kpkt and they hadn't been calculated already @@ -471,13 +396,16 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double const auto T_e = grid::get_Te(modelgridindex); double deltat = 0.; if (nts < globals::n_kpktdiffusion_timesteps) { - deltat = globals::kpktdiffusion_timescale * globals::time_step[nts].width; + deltat = globals::kpktdiffusion_timescale * globals::timesteps[nts].width; } // double deltat = 1. / (nne * 1.02e-12 * pow(T_e / 1e4, 0.843)); // printout("kpkt diffusion time simple %g, advanced %g\n", deltat, 1 / (nne * 1.02e-12 * pow(T_e / 1e4, 0.843))); - double const t_current = t1 + deltat; + const double t_current = t1 + deltat; - if (t_current <= t2) { + if (t_current > t2) { + vec_scale(pkt_ptr->pos, t2 / t1); + pkt_ptr->prop_time = t2; + } else { vec_scale(pkt_ptr->pos, t_current / t1); pkt_ptr->prop_time = t_current; @@ -525,13 +453,16 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double // printout("element %d, ion %d, coolingsum %g\n",element,ion,coolingsum); const int ilow = get_coolinglistoffset(element, ion); - const int ihigh = ilow + get_ncoolingterms(element, ion) - 1; + const int ihigh = ilow + get_ncoolingterms_ion(element, ion) - 1; // printout("element %d, ion %d, low %d, high %d\n",element,ion,low,high); if (globals::cellhistory[tid].cooling_contrib[ilow] < 0.) { // printout("calculate kpkt rates on demand modelgridindex %d element %d ion %d ilow %d ihigh %d // oldcoolingsum %g\n", // modelgridindex, element, ion, ilow, high, oldcoolingsum); - calculate_kpkt_rates_ion(modelgridindex, element, ion, ilow, tid); + const double C_ion = calculate_cooling_rates_ion(modelgridindex, element, ion, ilow, tid, nullptr, nullptr, + nullptr, nullptr); + // we just summed up every individual cooling process. make sure it matches the stored total for the ion + assert_always(C_ion == grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]); } // with the ion selected, we now select a level and transition type @@ -548,12 +479,12 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double printout("do_kpkt: error occured while selecting a cooling channel: low %d, high %d, i %d, rndcool %g\n", ilow, ihigh, i, rndcool_ion_process); printout("element %d, ion %d, offset %d, terms %d, coolingsum %g\n", element, ion, - get_coolinglistoffset(element, ion), get_ncoolingterms(element, ion), coolingsum); + get_coolinglistoffset(element, ion), get_ncoolingterms_ion(element, ion), coolingsum); printout("lower %g, %g, %g\n", globals::cellhistory[tid].cooling_contrib[get_coolinglistoffset(element, ion) - 1], globals::cellhistory[tid].cooling_contrib[get_coolinglistoffset(element, ion)], globals::cellhistory[tid].cooling_contrib[get_coolinglistoffset(element, ion) + 1]); - int const finalpos = get_coolinglistoffset(element, ion) + get_ncoolingterms(element, ion) - 1; + const int finalpos = get_coolinglistoffset(element, ion) + get_ncoolingterms_ion(element, ion) - 1; printout("upper %g, %g, %g\n", globals::cellhistory[tid].cooling_contrib[finalpos - 1], globals::cellhistory[tid].cooling_contrib[finalpos], globals::cellhistory[tid].cooling_contrib[finalpos + 1]); @@ -577,22 +508,17 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double assert_always(std::isfinite(pkt_ptr->nu_cmf)); /// and then emitt the packet randomly in the comoving frame - emitt_rpkt(pkt_ptr); + emit_rpkt(pkt_ptr); pkt_ptr->next_trans = 0; /// FLAG: transition history here not important, cont. process stats::increment(stats::COUNTER_K_STAT_TO_R_FF); - pkt_ptr->interactions += 1; pkt_ptr->last_event = 6; pkt_ptr->emissiontype = EMTYPE_FREEFREE; vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->nscatterings = 0; - if constexpr (VPKT_ON) { - // generate a virtual packet - int const realtype = 2; - vpkt_call_estimators(pkt_ptr, t_current, realtype); - } + vpkt_call_estimators(pkt_ptr, TYPE_KPKT); } else if (coolinglist[i].type == COOLINGTYPE_FB) { /// The k-packet converts directly into a r-packet by free-bound-emission. /// Need to select the r-packets frequency and a random direction in the @@ -601,35 +527,29 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double const int lowerion = coolinglist[i].ion; const int lowerlevel = coolinglist[i].level; const int upper = coolinglist[i].upperlevel; - // const double nu_threshold = get_phixs_threshold(element, ion, lowerlevel, phixstargetindex) /// then randomly sample the packets frequency according to the continuums /// energy distribution // Sample the packets comoving frame frequency according to paperII 4.2.2 - // zrand = rng_uniform(); - // if (zrand < 0.5) - { pkt_ptr->nu_cmf = select_continuum_nu(element, lowerion, lowerlevel, upper, T_e); } - // else - // { - // ///Emitt like a BB + // const double zrand = rng_uniform(); + // if (zrand < 0.5) { + pkt_ptr->nu_cmf = select_continuum_nu(element, lowerion, lowerlevel, upper, T_e); + // } else { + // // Emit like a BB // pkt_ptr->nu_cmf = sample_planck(T_e); // } // and then emitt the packet randomly in the comoving frame - emitt_rpkt(pkt_ptr); + emit_rpkt(pkt_ptr); if constexpr (TRACK_ION_STATS) { stats::increment_ion_stats(modelgridindex, element, lowerion + 1, stats::ION_RADRECOMB_KPKT, pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf); - const double escape_prob = get_rpkt_escape_prob(pkt_ptr, pkt_ptr->prop_time); - stats::increment_ion_stats(modelgridindex, element, lowerion + 1, stats::ION_RADRECOMB_ESCAPED, - pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf * escape_prob); } pkt_ptr->next_trans = 0; /// FLAG: transition history here not important, cont. process stats::increment(stats::COUNTER_K_STAT_TO_R_FB); - pkt_ptr->interactions += 1; pkt_ptr->last_event = 7; pkt_ptr->emissiontype = get_continuumindex(element, lowerion, lowerlevel, upper); pkt_ptr->trueemissiontype = pkt_ptr->emissiontype; @@ -637,11 +557,7 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->nscatterings = 0; - // call the estimator routine - generate a virtual packet - if constexpr (VPKT_ON) { - int const realtype = 2; - vpkt_call_estimators(pkt_ptr, t_current, realtype); - } + vpkt_call_estimators(pkt_ptr, TYPE_KPKT); } else if (coolinglist[i].type == COOLINGTYPE_COLLEXC) { /// the k-packet activates a macro-atom due to collisional excitation // printout("[debug] do_kpkt: k-pkt -> collisional excitation of MA\n"); @@ -661,8 +577,9 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double int upper = -1; // excitation to same ionization stage const int nuptrans = get_nuptrans(element, ion, level); + const auto *const uptrans = globals::elements[element].ions[ion].levels[level].uptrans; for (int ii = 0; ii < nuptrans; ii++) { - const int tmpupper = globals::elements[element].ions[ion].levels[level].uptrans[ii].targetlevelindex; + const int tmpupper = uptrans[ii].targetlevelindex; // printout(" excitation to level %d possible\n",upper); const double epsilon_trans = epsilon(element, ion, tmpupper) - epsilon_current; const double C = nnlevel * @@ -702,7 +619,6 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLEXC); stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLEXC); - pkt_ptr->interactions += 1; pkt_ptr->last_event = 8; pkt_ptr->trueemissiontype = EMTYPE_NOTSET; pkt_ptr->trueemissionvelocity = -1; @@ -725,7 +641,6 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLION); stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLION); - pkt_ptr->interactions += 1; pkt_ptr->last_event = 9; pkt_ptr->trueemissiontype = EMTYPE_NOTSET; pkt_ptr->trueemissionvelocity = -1; @@ -738,11 +653,8 @@ auto do_kpkt(struct packet *pkt_ptr, double t2, int nts) -> double abort(); } - return pkt_ptr->prop_time; + pkt_ptr->interactions++; } - vec_scale(pkt_ptr->pos, t2 / t1); - pkt_ptr->prop_time = t2; - return pkt_ptr->prop_time; } /*static int compare_coolinglistentry(const void *p1, const void *p2) diff --git a/kpkt.h b/kpkt.h index f8e8861a5..f7f46deda 100644 --- a/kpkt.h +++ b/kpkt.h @@ -11,8 +11,8 @@ namespace kpkt { void setup_coolinglist(); void calculate_cooling_rates(int modelgridindex, struct heatingcoolingrates *heatingcoolingrates); -double do_kpkt_bb(struct packet *pkt_ptr); -double do_kpkt(struct packet *pkt_ptr, double t2, int nts); +void do_kpkt_blackbody(struct packet *pkt_ptr); +void do_kpkt(struct packet *pkt_ptr, double t2, int nts); static inline int get_coolinglistoffset(int element, int ion) { return globals::elements[element].ions[ion].coolingoffset; diff --git a/light_curve.cc b/light_curve.cc index 7a41c374d..bb2bdb1ee 100644 --- a/light_curve.cc +++ b/light_curve.cc @@ -6,9 +6,10 @@ // Routine to make a MC light curve from the r-packets. -void write_light_curve(const std::string &lc_filename, const int current_abin, const double *light_curve_lum, - const double *light_curve_lumcmf, const int numtimesteps) { - assert_always(numtimesteps <= globals::ntstep); +void write_light_curve(const std::string &lc_filename, const int current_abin, + const std::vector &light_curve_lum, const std::vector &light_curve_lumcmf, + const int numtimesteps) { + assert_always(numtimesteps <= globals::ntimesteps); std::ofstream lc_file(lc_filename); if (!lc_file) { @@ -22,7 +23,7 @@ void write_light_curve(const std::string &lc_filename, const int current_abin, c /// Print out the UVOIR bolometric light curve. for (int nts = 0; nts < numtimesteps; nts++) { - assert_always(snprintf(linebuffer, maxlen, "%g %g %g", globals::time_step[nts].mid / DAY, + assert_always(snprintf(linebuffer, maxlen, "%g %g %g", globals::timesteps[nts].mid / DAY, (light_curve_lum[nts] / LSUN), (light_curve_lumcmf[nts] / LSUN)) < maxlen); lc_file << linebuffer << '\n'; } @@ -30,15 +31,16 @@ void write_light_curve(const std::string &lc_filename, const int current_abin, c if (current_abin == -1) { /// Now print out the gamma ray deposition rate in the same file. for (int m = 0; m < numtimesteps; m++) { - assert_always(snprintf(linebuffer, maxlen, "%g %g %g", globals::time_step[m].mid / DAY, - (globals::time_step[m].gamma_dep / LSUN / globals::time_step[m].width), - (globals::time_step[m].cmf_lum / globals::time_step[m].width / LSUN)) < maxlen); + assert_always(snprintf(linebuffer, maxlen, "%g %g %g", globals::timesteps[m].mid / DAY, + (globals::timesteps[m].gamma_dep / LSUN / globals::timesteps[m].width), + (globals::timesteps[m].cmf_lum / globals::timesteps[m].width / LSUN)) < maxlen); lc_file << linebuffer << '\n'; } } } -void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, double *light_curve_lum, double *light_curve_lumcmf) +void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, std::vector &light_curve_lum, + std::vector &light_curve_lumcmf) // add a packet to the outgoing light-curve. { if (current_abin == -1) { @@ -46,7 +48,7 @@ void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, double *light const double arrive_time = get_arrive_time(pkt_ptr); if (arrive_time > globals::tmin && arrive_time < globals::tmax) { const int nt = get_timestep(arrive_time); - safeadd(light_curve_lum[nt], pkt_ptr->e_rf / globals::time_step[nt].width / globals::nprocs); + safeadd(light_curve_lum[nt], pkt_ptr->e_rf / globals::timesteps[nt].width / globals::nprocs_exspec); } /// Now do the cmf light curve. @@ -54,7 +56,7 @@ void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, double *light const double arrive_time_cmf = get_arrive_time_cmf(pkt_ptr); if (arrive_time_cmf > globals::tmin && arrive_time_cmf < globals::tmax) { const int nt = get_timestep(arrive_time_cmf); - safeadd(light_curve_lumcmf[nt], pkt_ptr->e_cmf / globals::time_step[nt].width / globals::nprocs / + safeadd(light_curve_lumcmf[nt], pkt_ptr->e_cmf / globals::timesteps[nt].width / globals::nprocs_exspec / sqrt(1. - (globals::vmax * globals::vmax / CLIGHTSQUARED))); } @@ -62,10 +64,10 @@ void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, double *light } if (get_escapedirectionbin(pkt_ptr->dir, globals::syn_dir) == current_abin) { // Add only packets which escape to the current angle bin - double const t_arrive = get_arrive_time(pkt_ptr); + const double t_arrive = get_arrive_time(pkt_ptr); if (t_arrive > globals::tmin && t_arrive < globals::tmax) { - int const nt = get_timestep(t_arrive); - safeadd(light_curve_lum[nt], pkt_ptr->e_rf / globals::time_step[nt].width * MABINS / globals::nprocs); + const int nt = get_timestep(t_arrive); + safeadd(light_curve_lum[nt], pkt_ptr->e_rf / globals::timesteps[nt].width * MABINS / globals::nprocs_exspec); } } } \ No newline at end of file diff --git a/light_curve.h b/light_curve.h index 46c2c2647..b2cc4d5bc 100644 --- a/light_curve.h +++ b/light_curve.h @@ -2,12 +2,14 @@ #define LIGHT_CURVE_H #include +#include #include "exspec.h" -void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, double *light_curve_lum, double *light_curve_lumcmf); +void add_to_lc_res(const struct packet *pkt_ptr, int current_abin, std::vector &light_curve_lum, + std::vector &light_curve_lumcmf); -void write_light_curve(const std::string &lc_filename, int current_abin, const double *light_curve_lum, - const double *light_curve_lumcmf, int numtimesteps); +void write_light_curve(const std::string &lc_filename, int current_abin, const std::vector &light_curve_lum, + const std::vector &light_curve_lumcmf, int numtimesteps); #endif // LIGHT_CURVE_H diff --git a/ltepop.cc b/ltepop.cc index 5891c1c7b..c9dae7f07 100644 --- a/ltepop.cc +++ b/ltepop.cc @@ -1,5 +1,8 @@ #include "ltepop.h" +#include +#include + #include #include @@ -13,101 +16,41 @@ #include "sn3d.h" #include "update_grid.h" -auto nne_solution_f(double x, void *paras) -> double -/// For libgsl bracketing type solver -/// provides the equation which has to be solved to obtain the electron number -/// density (passed by x) -{ - const int modelgridindex = (static_cast(paras))->cellnumber; - const double rho = grid::get_rho(modelgridindex); - - double outersum = 0.; - // printout("debug get_nelements() %d =========================\n",get_nelements()); - for (int element = 0; element < get_nelements(); element++) { - const double abundance = grid::modelgrid[modelgridindex].composition[element].abundance; - if (abundance > 0 && get_nions(element) > 0) { - const double elem_meanweight = grid::get_element_meanweight(modelgridindex, element); - double innersum = 0.; - // printout("debug get_nions (element %d) %d =========================\n",element,get_nions(element)); - // uppermost_ion = globals::elements[element].uppermost_ion; - const int uppermost_ion = grid::get_elements_uppermost_ion(modelgridindex, element); - - auto ionfractions = std::make_unique(uppermost_ion + 1); - get_ionfractions(element, modelgridindex, x, ionfractions.get(), uppermost_ion); - - int ion = 0; - for (ion = 0; ion <= uppermost_ion; ion++) { - // printout("debug element %d, ion %d, ionfract(element,ion,T,x) %g\n",element,ion,ionfractions[ion]); - innersum += (get_ionstage(element, ion) - 1) * ionfractions[ion]; - } - assert_always(std::isfinite(innersum)); - outersum += abundance / elem_meanweight * innersum; - if (!std::isfinite(outersum)) { - printout("nne_solution_f: element %d ion %d uppermostion %d abundance %g, mass %g\n", element, ion, - grid::get_elements_uppermost_ion(modelgridindex, element), abundance, elem_meanweight); - printout("outersum %g\n", outersum); - abort(); - } - } - } - - return rho * outersum - x; -} - -void get_ionfractions(int element, int modelgridindex, double nne, double *ionfractions, int uppermost_ion) -// Calculate the fractions of an element's population in each ionization stage -// size of ionfractions array must be >= uppermostion + 1 -{ - assert_testmodeonly(modelgridindex < grid::get_npts_model()); - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(uppermost_ion < get_nions(element) || get_nions(element) == 0); - - auto nnionfactor = std::make_unique(uppermost_ion + 1); - nnionfactor[uppermost_ion] = 1; - - double denominator = 1.; - - for (int ion = uppermost_ion - 1; ion >= 0; ion--) { - nnionfactor[ion] = nnionfactor[ion + 1] * nne * phi(element, ion, modelgridindex); - denominator += nnionfactor[ion]; - } - - for (int ion = 0; ion <= uppermost_ion; ion++) { - const double numerator = nnionfactor[ion]; - ionfractions[ion] = numerator / denominator; - - if (!std::isfinite(ionfractions[ion])) { - if (modelgridindex != grid::get_npts_model()) { - printout("[warning] ionfract set to zero for ionstage %d of Z=%d in cell %d with T_e %g, T_R %g\n", - get_ionstage(element, ion), get_atomicnumber(element), modelgridindex, grid::get_Te(modelgridindex), - grid::get_TR(modelgridindex)); - // abort(); - ionfractions[ion] = 0; - } - } - // printout("ionfract(%d,%d,%d,%g) = %g\n", element, ion, modelgridindex, nne, ionfractions[ion]); - } -} +struct nne_solution_paras { + int modelgridindex; + bool force_lte; +}; static auto interpolate_ions_spontrecombcoeff(const int element, const int ion, const double T) -> double { assert_testmodeonly(element < get_nelements()); assert_testmodeonly(ion < get_nions(element)); assert_always(T >= MINTEMP); - int const lowerindex = floor(log(T / MINTEMP) / T_step_log); + const int lowerindex = floor(log(T / MINTEMP) / T_step_log); if (lowerindex < TABLESIZE - 1) { - int const upperindex = lowerindex + 1; - double const T_lower = MINTEMP * exp(lowerindex * T_step_log); - double const T_upper = MINTEMP * exp(upperindex * T_step_log); + const int upperindex = lowerindex + 1; + const double T_lower = MINTEMP * exp(lowerindex * T_step_log); + const double T_upper = MINTEMP * exp(upperindex * T_step_log); - double const f_upper = globals::elements[element].ions[ion].Alpha_sp[upperindex]; - double const f_lower = globals::elements[element].ions[ion].Alpha_sp[lowerindex]; + const double f_upper = globals::elements[element].ions[ion].Alpha_sp[upperindex]; + const double f_lower = globals::elements[element].ions[ion].Alpha_sp[lowerindex]; return f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T - T_lower); } return globals::elements[element].ions[ion].Alpha_sp[TABLESIZE - 1]; } -auto phi(const int element, const int ion, const int modelgridindex) -> double +static auto phi_lte(const int element, const int ion, const int modelgridindex) -> double { + // use Saha equation for LTE ionization balance + auto partfunc_ion = grid::modelgrid[modelgridindex].composition[element].partfunct[ion]; + auto partfunc_upperion = grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1]; + + const auto T_e = grid::get_Te(modelgridindex); + const double ionpot = epsilon(element, ion + 1, 0) - epsilon(element, ion, 0); + const double partfunct_ratio = partfunc_ion / partfunc_upperion; + return partfunct_ratio * SAHACONST * pow(T_e, -1.5) * exp(ionpot / KB / T_e); +} + +static auto phi_ion_equilib(const int element, const int ion, const int modelgridindex) -> double /// Calculates population ratio (a saha factor) of two consecutive ionisation stages /// in nebular approximation phi_j,k* = N_j,k*/(N_j+1,k* * nne) { @@ -115,185 +58,151 @@ auto phi(const int element, const int ion, const int modelgridindex) -> double assert_testmodeonly(element < get_nelements()); assert_testmodeonly(ion < get_nions(element)); - double phi = 0; + assert_testmodeonly(!globals::lte_iteration); + assert_testmodeonly(grid::modelgrid[modelgridindex].thick != 1); // should use use phi_lte instead - // double Y_nt, ionpot_in; - // int element_in, ion_in, nions_in; - // double rate_use; + assert_testmodeonly(!elem_has_nlte_levels(element)); // don't use this function if the NLTE solver is active + + auto partfunc_ion = grid::modelgrid[modelgridindex].composition[element].partfunct[ion]; + auto partfunc_upperion = grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1]; const auto T_e = grid::get_Te(modelgridindex); - // double T_R = grid::get_TR(modelgridindex); - - // double W = globals::cell[cellnumber].W; - - /// Old ionisation formula - // partfunct_ratio = - // globals::cell[cellnumber].composition[element].partfunct[ion]/globals::cell[cellnumber].composition[element].partfunct[ion+1]; - // phi = 1./W * sqrt(T_R/T_e) * partfunct_ratio * SAHACONST * pow(T_R,-1.5) * exp(ionpot/KB/T_R); - - /// New ionisation formula with zeta - // zeta = interpolate_zeta(element,ion,T_e); - // phi = 1./W * 1./(zeta+W*(1-zeta)) * sqrt(T_R/T_e) * partfunct_ratio * SAHACONST * pow(T_R,-1.5) * - // exp(ionpot/KB/T_R); - - /// Newest ionisation formula - - const bool use_lte_ratio = (globals::initial_iteration || grid::modelgrid[modelgridindex].thick == 1); - - if (use_lte_ratio) { - const double ionpot = epsilon(element, ion + 1, 0) - epsilon(element, ion, 0); - // printout("ionpot for element %d, ion %d is %g\n", element, ion, ionpot / EV); - const double partfunct_ratio = grid::modelgrid[modelgridindex].composition[element].partfunct[ion] / - grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1]; - phi = partfunct_ratio * SAHACONST * pow(T_e, -1.5) * exp(ionpot / KB / T_e); - } else - // elseif (NLTE_POPS_ALL_IONS_SIMULTANEOUS) - // { - // const float nne = grid::get_nne(modelgridindex); - // phi = ionstagepop(modelgridindex,element,ion) / ionstagepop(modelgridindex,element,ion+1) / nne; - // } - // else - { - double Gamma = 0.; - if constexpr (!USE_LUT_PHOTOION) { - Gamma = calculate_iongamma_per_gspop(modelgridindex, element, ion); - } else { - Gamma = - globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions() + element * get_max_nions() + ion]; - } - // printout("phicompare element %d ion %d T_e = %g gammaestimator %g calculate_iongamma_per_gspop %g\n", - // element, ion, T_e, - // globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions() + element * get_max_nions() + - // ion], calculate_iongamma_per_gspop(modelgridindex, element, ion)); - - // Gamma is the photoionization rate per ground level pop - const double Gamma_ion = - Gamma * stat_weight(element, ion, 0) / grid::modelgrid[modelgridindex].composition[element].partfunct[ion]; - - if (Gamma == 0. && (!NT_ON || (globals::rpkt_emiss[modelgridindex] == 0. && - grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(24, 48)) == 0. && - grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(28, 56)) == 0.))) { - printout("Fatal: Gamma = 0 for element %d, ion %d in phi ... abort\n", element, ion); - abort(); - } - // Alpha_st = stimrecombestimator[cellnumber*get_nelements()*get_max_nions()+element*get_max_nions()+ion]; - double const Alpha_st = 0.; /// approximate treatment neglects stimulated recombination + double Gamma = 0.; + if constexpr (USE_LUT_PHOTOION) { + Gamma = globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)]; + } else { + Gamma = calculate_iongamma_per_gspop(modelgridindex, element, ion); + } - double Alpha_sp = 0.; - if constexpr (NLTE_POPS_ON) { - Alpha_sp = - calculate_ionrecombcoeff(modelgridindex, T_e, element, ion + 1, false, false, false, false, false, false); - } else { - Alpha_sp = interpolate_ions_spontrecombcoeff(element, ion, T_e); - } + // Gamma is the photoionization rate per ground level pop + const double Gamma_ion = Gamma * stat_weight(element, ion, 0) / partfunc_ion; - // const double Col_rec = calculate_ionrecombcoeff(modelgridindex, T_e, element, ion + 1, false, true, false, false, - // false); - const double Col_rec = 0.; + if (Gamma == 0. && (!NT_ON || (globals::rpkt_emiss[modelgridindex] == 0. && + grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(24, 48)) == 0. && + grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(28, 56)) == 0.))) { + printout("Fatal: Gamma = 0 for element %d, ion %d in phi ... abort\n", element, ion); + abort(); + } - double Y_nt = 0.0; + const double Alpha_sp = interpolate_ions_spontrecombcoeff(element, ion, T_e); - if constexpr (NT_ON) { - Y_nt = nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion); - } + // const double Col_rec = calculate_ionrecombcoeff(modelgridindex, T_e, element, ion + 1, false, true, false, false, + // false); + const double Col_rec = 0.; - // || !std::isfinite(Gamma)) - // return phi_lte(element,ion,cellnumber); - // gamma_lte = interpolate_photoioncoeff_below(element,ion,0,T_e) + - // interpolate_photoioncoeff_above(element,ion,0,T_e); zeta = interpolate_zeta(element,ion,T_e); alpha_sp = - // interpolate_spontrecombcoeff(element,ion,0,T_e); phi = gamma_lte*(Alpha_sp+Apha_st)/(Gamma*alpha_sp) * - // partfunct_ratio * SAHACONST * pow(T_e,-1.5) * exp(ionpot/KB/T_e); + const double gamma_nt = NT_ON ? nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion) : 0.; - phi = (Alpha_sp + Alpha_st + Col_rec) / (Gamma_ion + Y_nt); + const double phi = (Alpha_sp + Col_rec) / (Gamma_ion + gamma_nt); - // Y_nt should generally be higher than the Gamma term for nebular epoch + // Y_nt should generally be higher than the Gamma term for nebular epoch - if (!std::isfinite(phi) || phi == 0.) { - printout( - "[fatal] phi: phi %g exceeds numerically possible range for element %d, ion %d, T_e %g ... remove higher or " - "lower ionisation stages\n", - phi, element, ion, T_e); - printout("[fatal] phi: Alpha_sp %g, Alpha_st %g, Gamma %g, partfunct %g, stat_weight %g\n", Alpha_sp, Alpha_st, - Gamma, grid::modelgrid[modelgridindex].composition[element].partfunct[ion], - stat_weight(element, ion, 0)); - printout("[fatal] phi: upperionpartfunct %g, upperionstatweight %g\n", - grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1], - stat_weight(element, ion + 1, 0)); - printout("[fatal] phi: Y_nt %g Col_rec %g grid::get_nne(modelgridindex) %g\n", Y_nt, Col_rec, - grid::get_nne(modelgridindex)); - // abort(); - } + if (!std::isfinite(phi) || phi == 0.) { + printout( + "[fatal] phi: phi %g exceeds numerically possible range for element %d, ion %d, T_e %g ... remove higher or " + "lower ionisation stages\n", + phi, element, ion, T_e); + printout("[fatal] phi: Alpha_sp %g, Gamma %g, partfunct %g, stat_weight %g\n", Alpha_sp, Gamma, partfunc_ion, + stat_weight(element, ion, 0)); + printout("[fatal] phi: upperionpartfunct %g, upperionstatweight %g\n", partfunc_upperion, + stat_weight(element, ion + 1, 0)); + printout("[fatal] phi: gamma_nt %g Col_rec %g grid::get_nne(modelgridindex) %g\n", gamma_nt, Col_rec, + grid::get_nne(modelgridindex)); + abort(); } return phi; } -// double phi_lte(int element, int ion, int cellnumber) -/// Calculates population ratio (a saha factor) of two consecutive ionisation stages -/// in nebular approximation phi_j,k* = N_j,k*/N_j+1,k* * nne -/*{ - double partfunct_ratio; - double phi; +[[nodiscard]] auto calculate_ionfractions(const int element, const int modelgridindex, const double nne, + const bool use_phi_lte) -> std::vector +// Calculate the fractions of an element's population in each ionization stage based on Saha LTE or ionisation +// equilibrium +{ + const int uppermost_ion = grid::get_elements_uppermost_ion(modelgridindex, element); + assert_testmodeonly(modelgridindex < grid::get_npts_model()); + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(uppermost_ion <= std::max(0, get_nions(element) - 1)); - double ionpot = epsilon(element,ion+1,0) - epsilon(element,ion,0); - float T_e = globals::cell[cellnumber].T_e; + if (uppermost_ion < 0) { + return {}; + } + + std::vector ionfractions(uppermost_ion + 1); + ionfractions[uppermost_ion] = 1; - partfunct_ratio = -globals::cell[cellnumber].composition[element].partfunct[ion]/globals::cell[cellnumber].composition[element].partfunct[ion+1]; - phi = partfunct_ratio * SAHACONST * pow(T_e,-1.5) * exp(ionpot/KB/T_e); + double normfactor = 1.; - if (!std::isfinite(phi)) - { - printout("phi_lte: phi %g exceeds numerically possible range for element %d, ion %d, T_e %g, ... remove higher or -lower ionisation stages\n",phi,element,ion,T_e); abort(); + for (int ion = uppermost_ion - 1; ion >= 0; ion--) { + const auto phifactor = + use_phi_lte ? phi_lte(element, ion, modelgridindex) : phi_ion_equilib(element, ion, modelgridindex); + ionfractions[ion] = ionfractions[ion + 1] * nne * phifactor; + normfactor += ionfractions[ion]; } - return phi; -}*/ -/* -double calculate_ltepartfunct(int element, int ion, double T) -/// Calculates the LTE partition function for ion=ion of element=element at -/// temperature T -{ - double U; - double epsilon_groundlevel; - double oneoverkbt; - int level; - int nlevels; - - epsilon_groundlevel = epsilon(element,ion,0); - oneoverkbt = 1/KB/T; - U = 0.; - nlevels = get_nlevels(element,ion); - for (level = 0; level < nlevels; level++) - { - U += stat_weight(element,ion,level) * exp(-(epsilon(element,ion,level)-epsilon_groundlevel)*oneoverkbt); - } - - if (!std::isfinite(U)) abort(); - return U; + for (int ion = 0; ion <= uppermost_ion; ion++) { + ionfractions[ion] = ionfractions[ion] / normfactor; + + if (!std::isfinite(ionfractions[ion]) && modelgridindex != grid::get_npts_model()) { + printout("[warning] ionfract set to zero for ionstage %d of Z=%d in cell %d with T_e %g, T_R %g\n", + get_ionstage(element, ion), get_atomicnumber(element), modelgridindex, grid::get_Te(modelgridindex), + grid::get_TR(modelgridindex)); + ionfractions[ion] = 0; + } + } + return ionfractions; +} + +static auto get_element_nne_contrib(const int modelgridindex, const int element) + -> double { // calculate number density of the current element (abundances are given by mass) + const double nnelement = grid::get_elem_numberdens(modelgridindex, element); + // Use ionization fractions to calculate the free electron contributions + if (nnelement > 0) { + double nne = 0.; + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + const auto nnion = get_nnion(modelgridindex, element, ion); + const int ioncharge = get_ionstage(element, ion) - 1; + nne += ioncharge * nnion; + } + return nne; + } + return 0.; } -*/ -/* -double calculate_groundlevelpop(int element, int ion, double T, int cellnumber, double nne, double nnnextion) -///calculates ground level population for ion=ion of element=element at -///temperature T and electron number density nne -///further the total population number nnnextion of the next higher ionisation stage is needed +static auto nne_solution_f(double nne_assumed, void *voidparas) -> double +// assume a value for nne and then calculate the resulting nne +// the difference between the assumed and calculated nne is returned { - double partfunct(int element, int ion, double T); + const auto *paras = reinterpret_cast(voidparas); + const int modelgridindex = paras->modelgridindex; + const bool force_lte = paras->force_lte; - double deltaE = epsilon(element,ion+1,0) - epsilon(element,ion,0); - double n0; + double nne_after = 0.; // the resulting nne after setting the ion balance with nne_assumed + for (int element = 0; element < get_nelements(); element++) { + const double nnelement = grid::get_elem_numberdens(modelgridindex, element); + if (nnelement > 0 && get_nions(element) > 0) { + if (!force_lte && elem_has_nlte_levels(element)) { + // populations from the NLTE solver are fixed during the nne solver + nne_after += get_element_nne_contrib(modelgridindex, element); + } else { + const bool use_phi_lte = force_lte || FORCE_SAHA_ION_BALANCE(get_atomicnumber(element)); + const auto ionfractions = calculate_ionfractions(element, modelgridindex, nne_assumed, use_phi_lte); + const int uppermost_ion = static_cast(ionfractions.size() - 1); + for (int ion = 0; ion <= uppermost_ion; ion++) { + const double nnion = nnelement * ionfractions[ion]; + const int ioncharge = get_ionstage(element, ion) - 1; + nne_after += ioncharge * nnion; + } + } - //n0 = nnnextion * nne * stat_weight(element,ion,0)/partfunct(element,ion+1,T) * C * pow(T,-1.5) * exp(deltaE/KB/T); - n0 = nnnextion * nne * stat_weight(element,ion,0)/globals::cell[cellnumber].composition[element].partfunct[ion+1] * -SAHACONST * pow(T,-1.5) * exp(deltaE/KB/T); + assert_always(std::isfinite(nne_after)); + } + } + nne_after = std::max(MINPOP, nne_after); - return n0; + return nne_after - nne_assumed; } -*/ auto get_groundlevelpop(int modelgridindex, int element, int ion) -> double /// Returns the given ions groundlevel population for modelgridindex which was precalculated @@ -302,9 +211,6 @@ auto get_groundlevelpop(int modelgridindex, int element, int ion) -> double assert_testmodeonly(modelgridindex < grid::get_npts_model()); assert_testmodeonly(element < get_nelements()); assert_testmodeonly(ion < get_nions(element)); - // double nn = grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]; - // if (nn < MINPOP) nn = MINPOP; - // return nn; const double nn = grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]; if (nn < MINPOP) { @@ -323,19 +229,18 @@ auto calculate_levelpop_lte(int modelgridindex, int element, int ion, int level) assert_testmodeonly(element < get_nelements()); assert_testmodeonly(ion < get_nions(element)); assert_testmodeonly(level < get_nlevels(element, ion)); + + const auto nnground = get_groundlevelpop(modelgridindex, element, ion); if (level == 0) { - return get_groundlevelpop(modelgridindex, element, ion); + return nnground; } - const double T_exc = LTEPOP_EXCITATION_USE_TJ ? grid::get_TJ(modelgridindex) : grid::get_Te(modelgridindex); - const double W = 1.; + const auto T_exc = LTEPOP_EXCITATION_USE_TJ ? grid::get_TJ(modelgridindex) : grid::get_Te(modelgridindex); - const double E_level = epsilon(element, ion, level); - const double E_ground = epsilon(element, ion, 0); - const double nnground = get_groundlevelpop(modelgridindex, element, ion); + const double E_aboveground = epsilon(element, ion, level) - epsilon(element, ion, 0); - return (nnground * W * stat_weight(element, ion, level) / stat_weight(element, ion, 0) * - exp(-(E_level - E_ground) / KB / T_exc)); + return (nnground * stat_weight(element, ion, level) / stat_weight(element, ion, 0) * + exp(-E_aboveground / KB / T_exc)); } static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion, int level, bool *skipminpop) @@ -347,13 +252,10 @@ static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion double nn = NAN; - // T_exc = MINTEMP; - if (level == 0) { nn = get_groundlevelpop(modelgridindex, element, ion); - } else if constexpr (NLTE_POPS_ON) { + } else if (elem_has_nlte_levels(element)) { if (is_nlte(element, ion, level)) { - // printout("Using an nlte population!\n"); const double nltepop_over_rho = grid::modelgrid[modelgridindex].nlte_pops[globals::elements[element].ions[ion].first_nlte + level - 1]; if (nltepop_over_rho < -0.9) { @@ -372,8 +274,8 @@ static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion *skipminpop = true; return nn; } - } else // level is in the superlevel - { + } else { + // level is in the superlevel assert_testmodeonly(level_isinsuperlevel(element, ion, level)); const int sl_nlte_index = globals::elements[element].ions[ion].first_nlte + get_nlevels_nlte(element, ion); @@ -412,7 +314,6 @@ auto calculate_levelpop(int modelgridindex, int element, int ion, int level) -> if (!skipminpop && nn < MINPOP) { if (grid::get_elem_abundance(modelgridindex, element) > 0) { nn = MINPOP; - // nn = calculate_levelpop_lte(modelgridindex, element, ion, level); } else { nn = 0.; } @@ -438,7 +339,7 @@ auto get_levelpop(int modelgridindex, int element, int ion, int level) -> double return nn; } -auto calculate_partfunct(int element, int ion, int modelgridindex) -> double +static auto calculate_partfunct(int element, int ion, int modelgridindex) -> double /// Calculates the partition function for ion=ion of element=element in /// cell modelgridindex { @@ -448,18 +349,16 @@ auto calculate_partfunct(int element, int ion, int modelgridindex) -> double double pop_store = NAN; // double E_level, E_ground, test; - int initial = 0; + bool initial = false; if (get_groundlevelpop(modelgridindex, element, ion) < MINPOP) { // either there reall is none of this ion or this is a first pass through // in either case, we won't have any real nlte_populations so the actual value of // of groundlevelpop for this calculation doesn't matter, so long as it's not zero! pop_store = get_groundlevelpop(modelgridindex, element, ion); - initial = 1; + initial = true; grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = 1.0; } - // printout("groundlevelpop %g\n", get_groundlevelpop(modelgridindex,element,ion)); - double U = 1.; const int nlevels = get_nlevels(element, ion); @@ -467,7 +366,6 @@ auto calculate_partfunct(int element, int ion, int modelgridindex) -> double for (int level = 1; level < nlevels; level++) { bool skipminpop = false; const double nn = calculate_levelpop_nominpop(modelgridindex, element, ion, level, &skipminpop) / groundpop; - // const double nn = get_levelpop(modelgridindex, element, ion, level) / groundpop; U += nn; } U *= stat_weight(element, ion, 0); @@ -480,7 +378,7 @@ auto calculate_partfunct(int element, int ion, int modelgridindex) -> double abort(); } - if (initial == 1) { + if (initial) { // put back the zero, just in case it matters for something grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = pop_store; } @@ -488,6 +386,21 @@ auto calculate_partfunct(int element, int ion, int modelgridindex) -> double return U; } +void calculate_cellpartfuncts(const int modelgridindex, const int element) +/// The partition functions depend only on T_R and W. This means they don't +/// change during any iteration on T_e. Therefore their precalculation was +/// taken out of calculate_ion_balance_nne to save runtime. +// TODO: not true if LTEPOP_EXCITATION_USE_TJ is true unless LTE mode only (TJ=TR=Te) +{ + /// Precalculate partition functions for each ion in every cell + /// this saves a factor 10 in calculation time of Saha-Boltzman populations + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + grid::modelgrid[modelgridindex].composition[element].partfunct[ion] = + calculate_partfunct(element, ion, modelgridindex); + } +} + auto calculate_sahafact(int element, int ion, int level, int upperionlevel, double T, double E_threshold) -> double /// calculates saha factor in LTE: Phi_level,ion,element = nn_level,ion,element/(nne*nn_upper,ion+1,element) { @@ -498,18 +411,235 @@ auto calculate_sahafact(int element, int ion, int level, int upperionlevel, doub // E_threshold, sf,stat_weight(element,ion,level),stat_weight(element,ion+1,0) ); if (sf < 0) { printout( - "[fatal] calculate_sahafact: Negative Saha factor. sfac %g element %d ion %d level %d upperionlevel %d g_lower " - "%g g_upper %g T %g E_threshold %g exppart %g\n", + "[fatal] calculate_sahafact: Negative Saha factor. sfac %g element %d ion %d level %d upperionlevel %d " + "g_lower %g g_upper %g T %g E_threshold %g exppart %g\n", sf, element, ion, level, upperionlevel, g_lower, g_upper, T, E_threshold, exp(E_threshold / KB / T)); abort(); } return sf; } -auto ionstagepop(int modelgridindex, int element, int ion) -> double -/// Calculates the given ionstages total population in nebular approximation for modelgridindex -/// The precalculated ground level population and partition function are used. +auto get_nnion(int modelgridindex, int element, int ion) -> double +/// Use the ground level population and partition function to get an ion population { return get_groundlevelpop(modelgridindex, element, ion) * grid::modelgrid[modelgridindex].composition[element].partfunct[ion] / stat_weight(element, ion, 0); } + +static auto find_uppermost_ion(const int modelgridindex, const int element, const double nne_hi, const bool force_lte) + -> int { + const int nions = get_nions(element); + if (nions == 0) { + return -1; + } + if (!force_lte && elem_has_nlte_levels(element)) { + return nions - 1; + } + + const bool use_lte = force_lte || FORCE_SAHA_ION_BALANCE(get_atomicnumber(element)); + int uppermost_ion = 0; + + if (force_lte) { + uppermost_ion = nions - 1; + } else { + int ion = -1; + for (ion = 0; ion < nions - 1; ion++) { + if (iongamma_is_zero(modelgridindex, element, ion) && + (!NT_ON || ((globals::rpkt_emiss[modelgridindex] == 0.) && + (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(24, 48)) == 0.) && + (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(28, 56)) == 0.)))) { + break; + } + } + uppermost_ion = ion; + } + + double factor = 1.; + int ion = 0; + for (ion = 0; ion < uppermost_ion; ion++) { + const auto phifactor = + use_lte ? phi_lte(element, ion, modelgridindex) : phi_ion_equilib(element, ion, modelgridindex); + factor *= nne_hi * phifactor; + + if (!std::isfinite(factor)) { + printout( + "[info] calculate_ion_balance_nne: uppermost_ion limited by phi factors for element " + "Z=%d, ionstage %d in cell %d\n", + get_atomicnumber(element), get_ionstage(element, ion), modelgridindex); + return ion; + } + } + uppermost_ion = ion; + return uppermost_ion; +} + +static void set_calculated_nne(const int modelgridindex) { + double nne = 0.; // free electron density + + for (int element = 0; element < get_nelements(); element++) { + nne += get_element_nne_contrib(modelgridindex, element); + } + + grid::set_nne(modelgridindex, std::max(MINPOP, nne)); +} + +void set_groundlevelpops(const int modelgridindex, const int element, const float nne, const bool force_lte) { + /// If not already set by the NLTE solver, set the ground level populations from either Saha LTE or + /// ionization/recombination balance (Photoionization Equilibrium) + const int nions = get_nions(element); + + if (nions <= 0) { + return; + } + + /// calculate number density of the current element (abundances are given by mass) + const double nnelement = grid::get_elem_numberdens(modelgridindex, element); + + const auto ionfractions = + (nnelement > 0) ? calculate_ionfractions(element, modelgridindex, nne, force_lte) : std::vector(); + + const int uppermost_ion = static_cast(ionfractions.size() - 1); + + /// Use ion fractions to calculate the groundlevel populations + for (int ion = 0; ion < nions; ion++) { + double nnion = NAN; + if (ion <= uppermost_ion) { + if (nnelement > 0) { + nnion = std::max(MINPOP, nnelement * ionfractions[ion]); + } else { + nnion = 0.; + } + } else { + nnion = MINPOP; + } + + const double groundpop = + nnion * stat_weight(element, ion, 0) / grid::modelgrid[modelgridindex].composition[element].partfunct[ion]; + + if (!std::isfinite(groundpop)) { + printout("[warning] calculate_ion_balance_nne: groundlevelpop infinite in connection with MINPOP\n"); + } + + grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = groundpop; + } +} + +static void set_groundlevelpops_neutral(const int modelgridindex) { + /// Special case of only neutral ions, set nne to some finite value that + /// packets are not lost in kpkts + printout("[warning] calculate_ion_balance_nne: only neutral ions in cell modelgridindex %d\n", modelgridindex); + for (int element = 0; element < get_nelements(); element++) { + const auto nnelement = grid::get_elem_numberdens(modelgridindex, element); + const int nions = get_nions(element); + /// Assign the species population to the neutral ion and set higher ions to MINPOP + for (int ion = 0; ion < nions; ion++) { + double nnion = NAN; + if (ion == 0) { + nnion = nnelement; + } else if (nnelement > 0.) { + nnion = MINPOP; + } else { + nnion = 0.; + } + const double groundpop = + (nnion * stat_weight(element, ion, 0) / grid::modelgrid[modelgridindex].composition[element].partfunct[ion]); + + grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = groundpop; + } + } +} + +static auto find_converged_nne(const int modelgridindex, double nne_hi, const bool force_lte) -> float { + /// Search solution for nne in [nne_lo,nne_hi] + + struct nne_solution_paras paras = {.modelgridindex = modelgridindex, .force_lte = force_lte}; + gsl_function f = {.function = &nne_solution_f, .params = ¶s}; + + double nne_lo = 0.; // MINPOP; + if (nne_solution_f(nne_lo, f.params) * nne_solution_f(nne_hi, f.params) > 0) { + const auto T_R = grid::get_TR(modelgridindex); + const auto T_e = grid::get_Te(modelgridindex); + const auto W = grid::get_W(modelgridindex); + printout("n, nne_lo, nne_hi, T_R, T_e, W, rho %d, %g, %g, %g, %g, %g, %g\n", modelgridindex, nne_lo, nne_hi, T_R, + T_e, W, grid::get_rho(modelgridindex)); + printout("nne@x_lo %g\n", nne_solution_f(nne_lo, f.params)); + printout("nne@x_hi %g\n", nne_solution_f(nne_hi, f.params)); + + for (int element = 0; element < get_nelements(); element++) { + printout("cell %d, element %d, uppermost_ion is %d\n", modelgridindex, element, + grid::get_elements_uppermost_ion(modelgridindex, element)); + + if constexpr (USE_LUT_PHOTOION) { + for (int ion = 0; ion <= grid::get_elements_uppermost_ion(modelgridindex, element); ion++) { + printout("element %d, ion %d, gammaionest %g\n", element, ion, + globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)]); + } + } + } + } + + double nne_solution = 0.; + + gsl_root_fsolver *solver = gsl_root_fsolver_alloc(gsl_root_fsolver_brent); + + gsl_root_fsolver_set(solver, &f, nne_lo, nne_hi); + constexpr int maxit = 50; + constexpr double fractional_accuracy = 1e-3; + int status = GSL_CONTINUE; + int iter = 0; + for (iter = 0; iter <= maxit; iter++) { + gsl_root_fsolver_iterate(solver); + nne_solution = gsl_root_fsolver_root(solver); + nne_lo = gsl_root_fsolver_x_lower(solver); + nne_hi = gsl_root_fsolver_x_upper(solver); + status = gsl_root_test_interval(nne_lo, nne_hi, 0, fractional_accuracy); + if (status != GSL_CONTINUE) { + break; + } + } + if (status == GSL_CONTINUE) { + printout("[warning] calculate_ion_balance_nne: nne did not converge within %d iterations\n", iter + 1); + } + + gsl_root_fsolver_free(solver); + + return std::max(MINPOP, nne_solution); +} + +auto calculate_ion_balance_nne(const int modelgridindex) -> void +/// Determines the electron number density for a given cell using one of +/// libgsl's root_solvers and calculates the depending level populations. +{ + const bool force_lte = globals::lte_iteration || grid::modelgrid[modelgridindex].thick == 1; + + const double nne_hi = grid::get_rho(modelgridindex) / MH; + + bool only_lowest_ionstage = true; // could be completely neutral, or just at each element's lowest ion stage + for (int element = 0; element < get_nelements(); element++) { + if (grid::get_elem_abundance(modelgridindex, element) > 0) { + const int uppermost_ion = find_uppermost_ion(modelgridindex, element, nne_hi, force_lte); + grid::set_elements_uppermost_ion(modelgridindex, element, uppermost_ion); + + only_lowest_ionstage = only_lowest_ionstage && (uppermost_ion <= 0); + } else { + grid::set_elements_uppermost_ion(modelgridindex, element, get_nions(element) - 1); + } + } + + if (only_lowest_ionstage) { + set_groundlevelpops_neutral(modelgridindex); + } else { + const auto nne_solution = find_converged_nne(modelgridindex, nne_hi, force_lte); + grid::set_nne(modelgridindex, nne_solution); + + for (int element = 0; element < get_nelements(); element++) { + // avoid overwriting the ground level populations set by the NLTE pop solver + const bool already_set_by_nlte_solver = !force_lte && elem_has_nlte_levels(element); + if (!already_set_by_nlte_solver) { + set_groundlevelpops(modelgridindex, element, nne_solution, force_lte); + } + } + } + + set_calculated_nne(modelgridindex); +} \ No newline at end of file diff --git a/ltepop.h b/ltepop.h index 1aeb12648..680e573ff 100644 --- a/ltepop.h +++ b/ltepop.h @@ -2,19 +2,21 @@ #define LTEPOP_H #include +#include #include "atomic.h" #include "sn3d.h" -double nne_solution_f(double x, void *paras); -void get_ionfractions(int element, int modelgridindex, double nne, double *ionfractions, int uppermost_ion); -double phi(int element, int ion, int modelgridindex); -double calculate_partfunct(int element, int ion, int modelgridindex); double get_groundlevelpop(int modelgridindex, int element, int ion); +double calculate_levelpop(int modelgridindex, int element, int ion, int level); double calculate_levelpop_lte(int modelgridindex, int element, int ion, int level); double get_levelpop(int modelgridindex, int element, int ion, int level); -double calculate_levelpop(int modelgridindex, int element, int ion, int level); double calculate_sahafact(int element, int ion, int level, int upperionlevel, double T, double E_threshold); -double ionstagepop(int modelgridindex, int element, int ion); +double get_nnion(int modelgridindex, int element, int ion); +void calculate_ion_balance_nne(int modelgridindex); +void calculate_cellpartfuncts(int modelgridindex, int element); +[[nodiscard]] auto calculate_ionfractions(const int element, const int modelgridindex, const double nne, + const bool force_lte) -> std::vector; +void set_groundlevelpops(const int modelgridindex, const int element, const float nne, const bool force_lte); #endif // LTEPOP_H diff --git a/macroatom.cc b/macroatom.cc index 9f12bf379..b0c52af7a 100644 --- a/macroatom.cc +++ b/macroatom.cc @@ -24,14 +24,16 @@ constexpr bool LOG_MACROATOM = false; static FILE *macroatom_file = nullptr; static void calculate_macroatom_transitionrates(const int modelgridindex, const int element, const int ion, - const int level, const double t_mid, struct chlevels *const chlevel) { + const int level, const double t_mid, struct chlevels &chlevel) { // printout("Calculating transition rates for element %d ion %d level %d\n", element, ion, level); - double *processrates = chlevel->processrates; + auto &processrates = chlevel.processrates; const auto T_e = grid::get_Te(modelgridindex); const auto nne = grid::get_nne(modelgridindex); const double epsilon_current = epsilon(element, ion, level); const double statweight = stat_weight(element, ion, level); + const auto &levelref = globals::elements[element].ions[ion].levels[level]; + /// Downward transitions within the current ionisation stage: /// radiative/collisional deexcitation and internal downward jumps processrates[MA_ACTION_RADDEEXC] = 0.; @@ -39,14 +41,15 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const processrates[MA_ACTION_INTERNALDOWNSAME] = 0.; const int ndowntrans = get_ndowntrans(element, ion, level); for (int i = 0; i < ndowntrans; i++) { - const int lower = globals::elements[element].ions[ion].levels[level].downtrans[i].targetlevelindex; - const auto A_ul = globals::elements[element].ions[ion].levels[level].downtrans[i].einstein_A; + const auto &downtransition = levelref.downtrans[i]; + const int lower = downtransition.targetlevelindex; + const auto A_ul = downtransition.einstein_A; const double epsilon_target = epsilon(element, ion, lower); const double epsilon_trans = epsilon_current - epsilon_target; const double R = rad_deexcitation_ratecoeff(modelgridindex, element, ion, level, lower, epsilon_trans, A_ul, statweight, t_mid); - const double C = col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, i); + const double C = col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, downtransition); const double individ_internal_down_same = (R + C) * epsilon_target; @@ -57,8 +60,8 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const processrates[MA_ACTION_COLDEEXC] += individ_col_deexc; processrates[MA_ACTION_INTERNALDOWNSAME] += individ_internal_down_same; - chlevel->sum_epstrans_rad_deexc[i] = processrates[MA_ACTION_RADDEEXC]; - chlevel->sum_internal_down_same[i] = processrates[MA_ACTION_INTERNALDOWNSAME]; + chlevel.sum_epstrans_rad_deexc[i] = processrates[MA_ACTION_RADDEEXC]; + chlevel.sum_internal_down_same[i] = processrates[MA_ACTION_INTERNALDOWNSAME]; // printout("checking downtrans %d to level %d: R %g, C %g, epsilon_trans %g\n",i,lower,R,C,epsilon_trans); } @@ -92,8 +95,9 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const processrates[MA_ACTION_INTERNALUPSAME] = 0.; const int nuptrans = get_nuptrans(element, ion, level); for (int i = 0; i < nuptrans; i++) { - const int upper = globals::elements[element].ions[ion].levels[level].uptrans[i].targetlevelindex; - const int lineindex = globals::elements[element].ions[ion].levels[level].uptrans[i].lineindex; + const auto &uptransition = globals::elements[element].ions[ion].levels[level].uptrans[i]; + const int upper = uptransition.targetlevelindex; + const int lineindex = uptransition.lineindex; const double epsilon_trans = epsilon(element, ion, upper) - epsilon_current; const double R = rad_excitation_ratecoeff(modelgridindex, element, ion, level, i, epsilon_trans, lineindex, t_mid); @@ -104,7 +108,7 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const const double individ_internal_up_same = (R + C + NT) * epsilon_current; processrates[MA_ACTION_INTERNALUPSAME] += individ_internal_up_same; - chlevel->sum_internal_up_same[i] = processrates[MA_ACTION_INTERNALUPSAME]; + chlevel.sum_internal_up_same[i] = processrates[MA_ACTION_INTERNALUPSAME]; } assert_always(std::isfinite(processrates[MA_ACTION_INTERNALUPSAME])); @@ -159,7 +163,6 @@ static void do_macroatom_raddeexcitation(struct packet *pkt_ptr, const int eleme const double rad_deexc, const int activatingline) { /// radiative deexcitation of MA: emitt rpkt /// randomly select which line transitions occurs - int linelistindex = -99; const int ndowntrans = get_ndowntrans(element, ion, level); double *sum_epstrans_rad_deexc = @@ -174,7 +177,7 @@ static void do_macroatom_raddeexcitation(struct packet *pkt_ptr, const int eleme const ptrdiff_t downtransindex = upperval - &sum_epstrans_rad_deexc[0]; assert_always(downtransindex < ndowntrans); - linelistindex = globals::elements[element].ions[ion].levels[level].downtrans[downtransindex].lineindex; + auto linelistindex = globals::elements[element].ions[ion].levels[level].downtrans[downtransindex].lineindex; if (linelistindex == activatingline) { stats::increment(stats::COUNTER_RESONANCESCATTERINGS); @@ -209,7 +212,7 @@ static void do_macroatom_raddeexcitation(struct packet *pkt_ptr, const int eleme pkt_ptr->last_event = 0; // emit the rpkt in a random direction - emitt_rpkt(pkt_ptr); + emit_rpkt(pkt_ptr); // the r-pkt can only interact with lines redder than the current one pkt_ptr->next_trans = linelistindex + 1; @@ -218,21 +221,18 @@ static void do_macroatom_raddeexcitation(struct packet *pkt_ptr, const int eleme pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->nscatterings = 0; - if constexpr (VPKT_ON) { - const int realtype = 3; - vpkt_call_estimators(pkt_ptr, pkt_ptr->prop_time, realtype); - } + vpkt_call_estimators(pkt_ptr, TYPE_MA); } static void do_macroatom_radrecomb(struct packet *pkt_ptr, const int modelgridindex, const int element, int *ion, int *level, const double rad_recomb) { const auto T_e = grid::get_Te(modelgridindex); - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); const double epsilon_current = epsilon(element, *ion, *level); const int upperion = *ion; const int upperionlevel = *level; /// Randomly select a continuum - double const zrand = rng_uniform(); + const double zrand = rng_uniform(); double rate = 0; const int nlevels = get_ionisinglevels(element, upperion - 1); int lower = 0; @@ -277,16 +277,11 @@ static void do_macroatom_radrecomb(struct packet *pkt_ptr, const int modelgridin pkt_ptr->last_event = 2; /// Finally emit the packet into a randomly chosen direction, update the continuum opacity and set some flags - emitt_rpkt(pkt_ptr); + emit_rpkt(pkt_ptr); if constexpr (TRACK_ION_STATS) { stats::increment_ion_stats(modelgridindex, element, upperion, stats::ION_RADRECOMB_MACROATOM, pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf); - - const double escape_prob = get_rpkt_escape_prob(pkt_ptr, pkt_ptr->prop_time); - - stats::increment_ion_stats(modelgridindex, element, upperion, stats::ION_RADRECOMB_ESCAPED, - pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf * escape_prob); } pkt_ptr->next_trans = 0; /// continuum transition, no restrictions for further line interactions @@ -295,16 +290,13 @@ static void do_macroatom_radrecomb(struct packet *pkt_ptr, const int modelgridin pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->nscatterings = 0; - if constexpr (VPKT_ON) { - const int realtype = 3; - vpkt_call_estimators(pkt_ptr, pkt_ptr->prop_time, realtype); - } + vpkt_call_estimators(pkt_ptr, TYPE_MA); } static void do_macroatom_ionisation(const int modelgridindex, const int element, int *ion, int *level, const double epsilon_current, const double internal_up_higher) { const auto T_e = grid::get_Te(modelgridindex); - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); int upper = -1; /// Randomly select the occuring transition @@ -339,17 +331,23 @@ static void do_macroatom_ionisation(const int modelgridindex, const int element, void do_macroatom(struct packet *pkt_ptr, const int timestep) /// Material for handling activated macro atoms. { + const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); + const auto T_e = grid::get_Te(modelgridindex); + + // EXPERIMENT: disable macroatom and emit according to blackbody + // kpkt::do_kpkt_blackbody(pkt_ptr); + // stats::increment(stats::COUNTER_RESONANCESCATTERINGS); + // pkt_ptr->interactions++; + // return; + const int tid = get_thread_num(); - const double t_mid = globals::time_step[timestep].mid; + const double t_mid = globals::timesteps[timestep].mid; // printout("[debug] do MA\n"); - const int cellindex = pkt_ptr->where; - const int modelgridindex = grid::get_cell_modelgridindex(cellindex); - const auto T_e = grid::get_Te(modelgridindex); - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); - assert_always(grid::modelgrid[modelgridindex].thick != 1); // macroatom should not be used in thick cells + assert_testmodeonly(grid::modelgrid[modelgridindex].thick != 1); // macroatom should not be used in thick cells /// calculate occupation number for active MA level //////////////////////////////////// /// general QUESTION: is it better to calculate the n_1 (later the n_ionstage and @@ -390,17 +388,17 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) // printout("[debug] %s Z=%d ionstage %d level %d, jumps %d\n", __func__, get_atomicnumber(element), // get_ionstage(element,ion), level, jumps); - assert_always(ion >= 0); - assert_always(ion < get_nions(element)); + assert_testmodeonly(ion >= 0); + assert_testmodeonly(ion < get_nions(element)); const double epsilon_current = epsilon(element, ion, level); // const int ndowntrans = get_ndowntrans(element, ion, level); const int nuptrans = get_nuptrans(element, ion, level); - assert_always(globals::cellhistory[tid].cellnumber == modelgridindex); + assert_testmodeonly(globals::cellhistory[tid].cellnumber == modelgridindex); - struct chlevels *chlevel = &globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; - const double *processrates = chlevel->processrates; + auto &chlevel = globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; + auto &processrates = chlevel.processrates; /// If there are no precalculated rates available then calculate them if (processrates[MA_ACTION_COLDEEXC] < 0) { calculate_macroatom_transitionrates(modelgridindex, element, ion, level, t_mid, chlevel); @@ -766,14 +764,14 @@ auto rad_excitation_ratecoeff(const int modelgridindex, const int element, const const double tau_sobolev = (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_current; if (tau_sobolev > 1e-100) { - double const beta = 1.0 / tau_sobolev * (-std::expm1(-tau_sobolev)); + const double beta = 1.0 / tau_sobolev * (-std::expm1(-tau_sobolev)); const double R_over_J_nu = n_l > 0. ? (B_lu - B_ul * n_u / n_l) * beta : B_lu * beta; R = R_over_J_nu * radfield::radfield(nu_trans, modelgridindex); if constexpr (DETAILED_LINE_ESTIMATORS_ON) { - if (!globals::initial_iteration) { + if (!globals::lte_iteration) { // check for a detailed line flux estimator to replace the binned/blackbody radiation field estimate const int jblueindex = radfield::get_Jblueindex(lineindex); if (jblueindex >= 0) { @@ -890,7 +888,7 @@ auto col_recombination_ratecoeff(const int modelgridindex, const int element, co const double sf = calculate_sahafact(element, upperion - 1, lower, upper, T_e, epsilon_trans); - double const C = nne * nne * sf * 1.55e13 * pow(T_e, -0.5) * g * sigma_bf * exp(-fac1) / fac1; + const double C = nne * nne * sf * 1.55e13 * pow(T_e, -0.5) * g * sigma_bf * exp(-fac1) / fac1; return C; } @@ -933,17 +931,16 @@ auto col_ionization_ratecoeff(const float T_e, const float nne, const int elemen } auto col_deexcitation_ratecoeff(const float T_e, const float nne, const double epsilon_trans, int element, int ion, - int upper, int downtransindex) -> double + int upper, const struct level_transition &downtransition) -> double // multiply by upper level population to get a rate per second { - const int lower = globals::elements[element].ions[ion].levels[upper].downtrans[downtransindex].targetlevelindex; + const int lower = downtransition.targetlevelindex; const double upperstatweight = stat_weight(element, ion, upper); const double lowerstatweight = stat_weight(element, ion, lower); - const double coll_str_thisline = - globals::elements[element].ions[ion].levels[upper].downtrans[downtransindex].coll_str; + const double coll_str_thisline = downtransition.coll_str; double C = 0.; if (coll_str_thisline < 0) { - const bool forbidden = globals::elements[element].ions[ion].levels[upper].downtrans[downtransindex].forbidden; + const bool forbidden = downtransition.forbidden; if (!forbidden) // alternative: (coll_strength > -1.5) i.e. to catch -1 { /// permitted E1 electric dipole transitions @@ -952,8 +949,7 @@ auto col_deexcitation_ratecoeff(const float T_e, const float nne, const double e // f = osc_strength(element,ion,upper,lower); // C = n_u * 2.16 * pow(fac1,-1.68) * pow(T_e,-1.5) * // stat_weight(element,ion,lower)/stat_weight(element,ion,upper) * nne * f; - const double trans_osc_strength = - globals::elements[element].ions[ion].levels[upper].downtrans[downtransindex].osc_strength; + const double trans_osc_strength = downtransition.osc_strength; const double eoverkt = epsilon_trans / (KB * T_e); /// Van-Regemorter formula, Mihalas (1978), eq.5-75, p.133 diff --git a/macroatom.h b/macroatom.h index c03c39f83..1bc6ccf6c 100644 --- a/macroatom.h +++ b/macroatom.h @@ -49,7 +49,7 @@ double col_ionization_ratecoeff(float T_e, float nne, int element, int ion, int double epsilon_trans); double col_deexcitation_ratecoeff(float T_e, float nne, double epsilon_trans, int element, int ion, int upper, - int downtransindex); + const struct level_transition &downtransition); double col_excitation_ratecoeff(float T_e, float nne, int element, int ion, int lower, int uptransindex, double epsilon_trans, double lowerstatweight); diff --git a/nltepop.cc b/nltepop.cc index ac7e816f7..94c3e516c 100644 --- a/nltepop.cc +++ b/nltepop.cc @@ -403,30 +403,35 @@ static void nltepop_reset_element(const int modelgridindex, const int element) { } } -static auto get_element_nlte_dimension_and_slpartfunc(const int modelgridindex, const int element, const int nions, - std::unique_ptr &superlevel_partfunc) -> int { - int nlte_dimension = 0; +static auto get_element_superlevelpartfuncs(const int modelgridindex, const int element) -> std::vector { + const int nions = get_nions(element); + std::vector superlevel_partfuncs(nions, 0.); for (int ion = 0; ion < nions; ion++) { - superlevel_partfunc[ion] = 0.; - const int nlevels_nlte = get_nlevels_nlte(element, ion); - - // this is the total number of nlte_levels (i.e. the size of the - // storage). Our rate matrix will need to be of this dimension +2: the - // ground state, the "super level". - // If there's no super level needed then we only need +1 if (ion_has_superlevel(element, ion)) { - nlte_dimension += nlevels_nlte + 2; + const int nlevels_nlte = get_nlevels_nlte(element, ion); const int nlevels = get_nlevels(element, ion); for (int level = nlevels_nlte + 1; level < nlevels; level++) { - superlevel_partfunc[ion] += superlevel_boltzmann(modelgridindex, element, ion, level); + superlevel_partfuncs[ion] += superlevel_boltzmann(modelgridindex, element, ion, level); } - // printout(" NLTE: including ion_stage %d, which contributes %d to the vector dimension (including superlevel - // with partfunc %g)\n", - // get_ionstage(element, ion), nlevels_nlte + 2, superlevel_partfunc[ion]); } else { - nlte_dimension += nlevels_nlte + 1; - // printout(" NLTE: including ion_stage %d, which contributes %d to the vector dimension (no super level)\n", - // get_ionstage(element, ion), nlevels_nlte + 1); + superlevel_partfuncs[ion] = 0.; + } + } + + return superlevel_partfuncs; +} + +static auto get_element_nlte_dimension(const int element) -> int { + int nlte_dimension = 0; + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + const int nlevels_nlte = get_nlevels_nlte(element, ion); + + nlte_dimension += nlevels_nlte + 1; // ground state is not counted in nlevels_nlte + + // add super level if it exists + if (ion_has_superlevel(element, ion)) { + nlte_dimension++; } } @@ -434,7 +439,7 @@ static auto get_element_nlte_dimension_and_slpartfunc(const int modelgridindex, } static void nltepop_matrix_add_boundbound(const int modelgridindex, const int element, const int ion, - const double t_mid, std::unique_ptr &s_renorm, + const double t_mid, const std::vector &s_renorm, gsl_matrix *rate_matrix_rad_bb, gsl_matrix *rate_matrix_coll_bb, gsl_matrix *rate_matrix_ntcoll_bb) { const auto T_e = grid::get_Te(modelgridindex); @@ -448,15 +453,17 @@ static void nltepop_matrix_add_boundbound(const int modelgridindex, const int el // de-excitation const int ndowntrans = get_ndowntrans(element, ion, level); for (int i = 0; i < ndowntrans; i++) { - const double A_ul = globals::elements[element].ions[ion].levels[level].downtrans[i].einstein_A; - const int lower = globals::elements[element].ions[ion].levels[level].downtrans[i].targetlevelindex; + const auto &downtransition = globals::elements[element].ions[ion].levels[level].downtrans[i]; + const double A_ul = downtransition.einstein_A; + const int lower = downtransition.targetlevelindex; const double epsilon_trans = epsilon_level - epsilon(element, ion, lower); const double R = rad_deexcitation_ratecoeff(modelgridindex, element, ion, level, lower, epsilon_trans, A_ul, statweight, t_mid) * s_renorm[level]; - const double C = col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, i) * s_renorm[level]; + const double C = + col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, downtransition) * s_renorm[level]; const int upper_index = level_index; const int lower_index = get_nlte_vector_index(element, ion, lower); @@ -523,7 +530,7 @@ static void nltepop_matrix_add_boundbound(const int modelgridindex, const int el } static void nltepop_matrix_add_ionisation(const int modelgridindex, const int element, const int ion, - std::unique_ptr &s_renorm, gsl_matrix *rate_matrix_rad_bf, + const std::vector &s_renorm, gsl_matrix *rate_matrix_rad_bf, gsl_matrix *rate_matrix_coll_bf) { assert_always(ion + 1 < get_nions(element)); // can't ionise the top ion const auto T_e = grid::get_Te(modelgridindex); @@ -582,7 +589,7 @@ static void nltepop_matrix_add_ionisation(const int modelgridindex, const int el } static void nltepop_matrix_add_nt_ionisation(const int modelgridindex, const int element, const int ion, - std::unique_ptr &s_renorm, gsl_matrix *rate_matrix_ntcoll_bf) { + const std::vector &s_renorm, gsl_matrix *rate_matrix_ntcoll_bf) { // collisional ionization by non-thermal electrons assert_always(ion + 1 < get_nions(element)); // can't ionise the top ion @@ -619,6 +626,7 @@ static void nltepop_matrix_normalise(const int modelgridindex, const int element // TODO: consider replacing normalisation by LTE populations with // GSL's gsl_linalg_balance_matrix(gsl_matrix * A, gsl_vector * D) function instead + set_groundlevelpops(modelgridindex, element, grid::get_nne(modelgridindex), true); for (size_t column = 0; column < nlte_dimension; column++) { int ion = -1; int level = -1; @@ -649,29 +657,10 @@ static void nltepop_matrix_normalise(const int modelgridindex, const int element } static void set_element_pops_lte(const int modelgridindex, const int element) { - nltepop_reset_element(modelgridindex, element); - - // const int nions = get_nions(element); - // for (int ion = 0; ion < nions; ion++) - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion] = calculate_partfunct(element, ion, - // modelgridindex); - // - // const float nne = grid::get_nne(modelgridindex); - // const double elem_meanweight = grid::get_element_meanweight(modelgridindex, element); - // const double nnelement = grid::get_elem_abundance(modelgridindex, element) / elem_meanweight * - // grid::get_rho(modelgridindex); for (int ion = 0; ion < nions; ion++) - // { - // double nnion; - // if (ion == 0) - // nnion = nnelement * ionfract(element, ion, modelgridindex, nne); - // else - // nnion = MINPOP; - // - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = ( - // nnion * stat_weight(element,ion,0) / grid::modelgrid[modelgridindex].composition[element].partfunct[ion]); - // - // assert_always(std::isfinite(grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion])); - // } + nltepop_reset_element(modelgridindex, element); // set NLTE pops as invalid so that LTE pops will be used instead + + calculate_cellpartfuncts(modelgridindex, element); + set_groundlevelpops(modelgridindex, element, grid::get_nne(modelgridindex), true); } static auto lumatrix_is_singular(const gsl_matrix *LU, const int element) -> bool { @@ -835,7 +824,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const const time_t sys_time_start_nltesolver = time(nullptr); - const double t_mid = globals::time_step[timestep].mid; + const double t_mid = globals::timesteps[timestep].mid; const int nions = get_nions(element); const double nnelement = grid::get_elem_numberdens(modelgridindex, element); @@ -844,15 +833,8 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const "population %.2e)\n", modelgridindex, timestep, nlte_iter, atomic_number, grid::get_elem_abundance(modelgridindex, element), nnelement); - // LTE test, make sure binned radfield is off - // grid::set_TR(modelgridindex,3000); - // grid::set_W(modelgridindex,1.0); - // printout("T_E %g T_R was %g, setting to 3000 \n",grid::get_Te(modelgridindex),get_TR(modelgridindex)); - - auto superlevel_partfunc = - std::make_unique(nions); // space is allocated for every ion, even if it does not have a superlevel - const int nlte_dimension = - get_element_nlte_dimension_and_slpartfunc(modelgridindex, element, nions, superlevel_partfunc); + auto superlevel_partfunc = std::vector(nions) = get_element_superlevelpartfuncs(modelgridindex, element); + const int nlte_dimension = get_element_nlte_dimension(element); // printout("NLTE: the vector dimension is %d", nlte_dimension); @@ -882,10 +864,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const gsl_vector *const balance_vector = gsl_vector_calloc(nlte_dimension); - if (balance_vector == nullptr) { - printout("Cannot allocate NLTE rate matrix/balance vector memory.\n"); - abort(); - } + assert_always(balance_vector != nullptr); // printout(" Adding rates for ion stages:"); for (int ion = 0; ion < nions; ion++) { @@ -895,7 +874,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const const int nlevels = get_nlevels(element, ion); const int nlevels_nlte = get_nlevels_nlte(element, ion); - auto s_renorm = std::make_unique(nlevels); + auto s_renorm = std::vector(nlevels); for (int level = 0; level <= nlevels_nlte; level++) { s_renorm[level] = 1.0; } @@ -934,6 +913,25 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const // set first balance vector entry to the element population (all other entries will be zero) gsl_vector_set(balance_vector, 0, nnelement); + if (FORCE_SAHA_ION_BALANCE(atomic_number)) { + const auto ionfractions = calculate_ionfractions(element, modelgridindex, grid::get_nne(modelgridindex), true); + const int uppermost_ion = static_cast(ionfractions.size() - 1); + for (int ion = 1; ion <= uppermost_ion; ion++) { + // replace matrix row for ion's ground state with sum of this ion's level populations is equal to the ion + // population + const double nnion = nnelement * ionfractions[ion]; + const int index_ion_ground = get_nlte_vector_index(element, ion, 0); + const int index_ion_toplevel = get_nlte_vector_index(element, ion, get_nlevels(element, ion)); + gsl_vector_view ion_ground_row_view = gsl_matrix_row(rate_matrix, index_ion_ground); + gsl_vector_set_all(&ion_ground_row_view.vector, 0.); + for (int index = index_ion_ground; index <= index_ion_toplevel; index++) { + gsl_vector_set(&ion_ground_row_view.vector, index, 1.); + } + + gsl_vector_set(balance_vector, get_nlte_vector_index(element, ion, index_ion_ground), nnion); + } + } + // calculate the normalisation factors and apply them to the matrix // columns and balance vector elements gsl_vector *pop_norm_factor_vec = gsl_vector_calloc(nlte_dimension); @@ -977,7 +975,6 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const assert_always(gsl_vector_get(popvec, index) >= 0.); } - // double ion_populations[nions]; for (int ion = 0; ion < nions; ion++) { const int nlevels_nlte = get_nlevels_nlte(element, ion); const int index_gs = get_nlte_vector_index(element, ion, 0); @@ -1005,76 +1002,22 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const const int index_sl = get_nlte_vector_index(element, ion, nlevels_nlte + 1); grid::modelgrid[modelgridindex].nlte_pops[nlte_start + nlevels_nlte] = (gsl_vector_get(popvec, index_sl) / grid::modelgrid[modelgridindex].rho / superlevel_partfunc[ion]); - - // printout("solve_nlte_pops_element: The Z=%d ionstage %d superlevel population is %g with rho %g and - // superlevel_partfunc %g Te %g scaled pop stored as %g\n", get_atomicnumber(element), get_ionstage(element, - // ion), gsl_vector_get(popvec, index_sl), grid::modelgrid[modelgridindex].rho, superlevel_partfunc[ion], - // grid::get_Te(modelgridindex), grid::modelgrid[modelgridindex].nlte_pops[nlte_start + nlevels_nlte]); the - // stored population is already divided by the partfunc, so just multiply it by the superlevel_boltzmann to get - // the population of a level in the SL - - // solution_ion_pop += gsl_vector_get(popvec, index_sl); } - // printout(" I had a ground level pop of %g, a part fn of %g and therefore an ion pop of %g\n", - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion], - // (grid::modelgrid[modelgridindex].composition[element].partfunct[ion] * - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] - // / stat_weight(element, ion, 0))); - - // ionstagepop here must be called before setting the new ground level population - // printout(" For ion_stage %d the total population is %g, but was previously %g\n", - // ion_stage,solution_ion_pop,ionstagepop(modelgridindex, element, ion)); // store the ground level population grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = gsl_vector_get(popvec, index_gs); // solution_ion_pop += gsl_vector_get(popvec, index_gs); - precalculate_partfuncts(modelgridindex); - - // ion_populations[ion] = solution_ion_pop; - // if (ion > 0) - // { - // const double gspopratio = grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion-1] / - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]; - // - // const double ionpot = epsilon(element,ion,0) - epsilon(element,ion-1,0); - // const auto T_e = grid::get_Te(modelgridindex); - // const double partfunct_ratio = grid::modelgrid[modelgridindex].composition[element].partfunct[ion-1] / - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion]; const double gs_g_ratio = - // stat_weight(element,ion-1,0) / stat_weight(element,ion,0); const double sbphi_gs = gs_g_ratio * SAHACONST * - // pow(T_e,-1.5) * exp(ionpot/KB/T_e) * grid::get_nne(modelgridindex); const double solution_ion_pop_ratio = - // ion_populations[ion-1] / ion_populations[ion]; const double sbphi = partfunct_ratio * SAHACONST * - // pow(T_e,-1.5) * exp(ionpot/KB/T_e) * grid::get_nne(modelgridindex); - // - // printout(" The ratio of groundlevel pops (ion %d)/(ion %d) is %g, Saha-Boltzmann value is %g ratio %g\n", - // get_ionstage(element,ion-1),ion_stage,gspopratio,sbphi_gs,gspopratio/sbphi_gs); - // printout(" The ratio of total pops (ion %d)/(ion %d) is %g, Saha-Boltzmann value is %g ratio %g\n", - // get_ionstage(element,ion-1),ion_stage,solution_ion_pop_ratio,sbphi,solution_ion_pop_ratio/sbphi); - // const float nne = grid::get_nne(modelgridindex); - // // calculate_partfunct(element, ion, modelgridindex) * - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] / stat_weight(element,ion,0)) - // printout(" The corresponding phi-factor is: %g\n", (solution_ion_pop_ratio / nne)); - // printout(" The ionization solver gives phi(ion_stage=%d / %d) = %g\n", - // get_ionstage(element, ion-1), get_ionstage(element, ion), phi(element, ion-1, modelgridindex)); - // } - - // double nne = grid::get_nne(modelgridindex); - // printout(" From ion fract, the ion pop should be %g\n", ionfract(element, ion, modelgridindex, - // nne)*nnelement); printout(" I think that the element population is: %g (from abundance %g and rho %g)\n", - // grid::get_elem_abundance(modelgridindex,element)/elem_meanweight*grid::get_rho(modelgridindex), - // grid::get_elem_abundance(modelgridindex,element), grid::get_rho(modelgridindex)); printout(" I currently think - // that the top ion is: %d\n", elements_uppermost_ion[tid][element]); + calculate_cellpartfuncts(modelgridindex, element); } - const double elem_pop_abundance = nnelement; const double elem_pop_matrix = gsl_blas_dasum(popvec); - const double elem_pop_error_percent = fabs((elem_pop_abundance / elem_pop_matrix) - 1) * 100; + const double elem_pop_error_percent = fabs((nnelement / elem_pop_matrix) - 1) * 100; if (elem_pop_error_percent > 1.0) { printout( " WARNING: The Z=%d element population is: %g (from abundance) and %g (from matrix solution sum of level " "pops), error: %.1f%%. Forcing element pops to LTE.\n", - atomic_number, elem_pop_abundance, elem_pop_matrix, elem_pop_error_percent); + atomic_number, nnelement, elem_pop_matrix, elem_pop_error_percent); set_element_pops_lte(modelgridindex, element); } @@ -1086,7 +1029,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const rate_matrix_ntcoll_bf); } - bool const print_detailed_level_stats = false; + const bool print_detailed_level_stats = false; // if ((atomic_number == 26) && ((timestep % 5) == 0) && (nlte_iter == 0)) // { @@ -1132,431 +1075,6 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const } } -// this does single ion solving and will be deprecated at some point -auto solve_nlte_pops_ion(int element, int ion, int modelgridindex, int timestep) -> double -// solves for nlte correction factors to level populations for levels -{ - if (!(get_nlevels(element, ion) > 1)) { - return 0.; - } - - double *rate_matrix = nullptr; - double *balance_vector = nullptr; - - double test_ratio = 0.; - - const auto T_e = grid::get_Te(modelgridindex); - const float nne = grid::get_nne(modelgridindex); - - printout( - "Solving for NLTE populations in cell %d. Doing Z=%d ionstage %d (element %d, ion %d). I think it's timestep " - "%d\n", - modelgridindex, get_atomicnumber(element), get_ionstage(element, ion), element, ion, timestep); - // printout("Current Te %g and ne %g\n",T_e, nne); - - const int nlevels_nlte = get_nlevels_nlte(element, ion); - const double t_mid = globals::time_step[timestep].mid; - - int nlte_size = 0; - bool super_level = false; - if (!ion_has_superlevel(element, ion)) { - nlte_size = nlevels_nlte + 2; - super_level = false; - } else { - nlte_size = nlevels_nlte + 3; - super_level = true; - } - // that's the total number of nlte_levels (i.e. the size of the - // storage). Our rate matrix will need to be of this dimension +3: the - // ground state, the "super level" and the ground state of the ion - // above. If there's no super level needed then we only need +2 - int const nlte_start = globals::elements[element].ions[ion].first_nlte; - - // if (get_groundlevelpop(modelgridindex,element,ion) > (1.2*MINPOP)) - if (grid::get_elem_abundance(modelgridindex, element) > 0.0) { - rate_matrix = static_cast(calloc(nlte_size * nlte_size, sizeof(double))); - assert_always(rate_matrix != nullptr); - - balance_vector = static_cast(calloc(nlte_size, sizeof(double))); - assert_always(balance_vector != nullptr); - - // printf("rate %p balance %p NULL %p\n", rate_matrix, balance_vector,nullptr); - // printout("I think there are %d levels to deal with and managed to allocate memory.\n", nlte_size); - - double superlevel_partition = 0.0; - for (int level = 1; level < get_nlevels(element, ion); level++) { - if (!is_nlte(element, ion, level)) { - printout("element %d ion %d level %d is in the superlevel\n", element, ion, level); - superlevel_partition += superlevel_boltzmann(modelgridindex, element, ion, level); - assert_always(std::isfinite(superlevel_partition)); - } - } - - double upperion_partition = 0.0; - if (ion < get_nions(element)) { - for (int level = 0; level < get_nlevels(element, ion + 1); level++) { - upperion_partition += get_levelpop(modelgridindex, element, ion + 1, level); - } - } - assert_always(std::isfinite(upperion_partition)); - - if (superlevel_partition > 0.0) { - printout("I found a superlevel and have computed a partition function for its substates of %g.\n", - superlevel_partition); - } else { - printout("I don't know about any super level for this case.\n"); - } - - const int nlevels = get_nlevels(element, ion); - const int nlevels_nlte = get_nlevels_nlte(element, ion); - auto s_renorm = std::make_unique(nlevels); - - for (int level = 0; level < nlevels; level++) { - if (level == 0 || (is_nlte(element, ion, level))) { - s_renorm[level] = 1.0; - } else { - s_renorm[level] = superlevel_boltzmann(modelgridindex, element, ion, level) / superlevel_partition; - } - } - - for (int level = 0; level < get_nlevels(element, ion); level++) { - const double epsilon_current = epsilon(element, ion, level); - const double statweight = stat_weight(element, ion, level); - - // deexcitation - const int ndowntrans = get_ndowntrans(element, ion, level); - for (int i = 0; i < ndowntrans; i++) { - const double A_ul = globals::elements[element].ions[ion].levels[level].downtrans[i].einstein_A; - const int lower = globals::elements[element].ions[ion].levels[level].downtrans[i].targetlevelindex; - const double epsilon_trans = epsilon_current - epsilon(element, ion, lower); - - const double R = rad_deexcitation_ratecoeff(modelgridindex, element, ion, level, lower, epsilon_trans, A_ul, - statweight, t_mid); - assert_always(std::isfinite(R)); - const double C = col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, i); - assert_always(std::isfinite(C)); - - int level_use = 0; - if ((level == 0) || is_nlte(element, ion, level)) { - level_use = level; - } else { - level_use = nlevels_nlte + 1; - } - - int lower_use = 0; - if ((lower == 0) || (is_nlte(element, ion, lower))) { - lower_use = lower; - } else { - lower_use = nlevels_nlte + 1; - } - - // printout("First using %d of %d\n", level_use*(nlte_size) + level_use, nlte_size*nlte_size); - // printout("Second using %d of %d\n", lower_use*(nlte_size) + level_use, nlte_size*nlte_size); - rate_matrix[level_use * nlte_size + level_use] -= (R + C) * s_renorm[level]; - rate_matrix[lower_use * nlte_size + level_use] += (R + C) * s_renorm[level]; - } - - // excitation - const int nuptrans = get_nuptrans(element, ion, level); - for (int i = 0; i < nuptrans; i++) { - // printout(" level %d excitation i %d\n", level, i); - const int lineindex = globals::elements[element].ions[ion].levels[level].uptrans[i].lineindex; - const struct linelist_entry *line = &globals::linelist[lineindex]; - const int upper = line->upperlevelindex; - const double epsilon_trans = epsilon(element, ion, upper) - epsilon_current; - - const double R = - rad_excitation_ratecoeff(modelgridindex, element, ion, level, i, epsilon_trans, lineindex, t_mid); - assert_always(std::isfinite(R)); - const double C = col_excitation_ratecoeff(T_e, nne, element, ion, level, i, epsilon_trans, statweight); - assert_always(std::isfinite(C)); - - int level_use = 0; - double s_renorm = 1.; - if ((level == 0) || (is_nlte(element, ion, level))) { - level_use = level; - s_renorm = 1.; - } else { - level_use = nlevels_nlte + 1; - s_renorm = superlevel_boltzmann(modelgridindex, element, ion, level) / superlevel_partition; - } - assert_always(std::isfinite(s_renorm)); - - int upper_use = 0; - if ((upper == 0) || (is_nlte(element, ion, upper))) { - upper_use = upper; - } else { - upper_use = nlevels_nlte + 1; - } - // printout("Third using %d of %d\n", level_use*(nlte_size) + level_use, nlte_size*nlte_size); - // printout("Fourth using %d of %d\n", upper_use*(nlte_size) + level_use, nlte_size*nlte_size); - rate_matrix[level_use * nlte_size + level_use] -= (R + C) * s_renorm; - rate_matrix[upper_use * nlte_size + level_use] += (R + C) * s_renorm; - } - - // printout("nt_ion\n"); - if (NT_ON && ion < get_nions(element) - 1) { - const double Y = nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion); - - int level_use = 0; - double s_renorm = 1.0; - if ((level == 0) || (is_nlte(element, ion, level))) { - level_use = level; - s_renorm = 1.0; - } else { - level_use = nlevels_nlte + 1; // the super level - s_renorm = superlevel_boltzmann(modelgridindex, element, ion, level) / superlevel_partition; - } - assert_always(std::isfinite(s_renorm)); - - const int upper_use = nlte_size - 1; // the continuum - rate_matrix[level_use * nlte_size + level_use] -= Y * s_renorm; - rate_matrix[upper_use * nlte_size + level_use] += Y * s_renorm; - } - - // now put in the photoionization/recombination processes - const int ionisinglevels = get_ionisinglevels(element, ion); - if ((ion < get_nions(element) - 1) && (level < ionisinglevels)) { - double s_renorm = 1.; - - int level_use = 0; - if ((level == 0) || (is_nlte(element, ion, level))) { - level_use = level; - } else { - level_use = nlevels_nlte + 1; // the super level - s_renorm = superlevel_boltzmann(modelgridindex, element, ion, level) / superlevel_partition; - } - assert_always(std::isfinite(s_renorm)); - - // printout("ionization\n"); - // ionization - const int upper_use = nlte_size - 1; // ion above - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { - const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); - const double epsilon_trans = epsilon(element, ion + 1, upper) - epsilon_current; - const double R = get_corrphotoioncoeff(element, ion, level, phixstargetindex, modelgridindex); - assert_always(std::isfinite(R)); - const double C = col_ionization_ratecoeff(T_e, nne, element, ion, level, phixstargetindex, epsilon_trans); - assert_always(std::isfinite(C)); - - rate_matrix[level_use * (nlte_size) + level_use] -= (R + C) * s_renorm; - rate_matrix[upper_use * (nlte_size) + level_use] += (R + C) * s_renorm; - } - - // printout("recombination\n"); - // recombination - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { - const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); - const double epsilon_trans = epsilon(element, ion + 1, upper) - epsilon_current; - const double R = rad_recombination_ratecoeff(T_e, nne, element, ion + 1, upper, level, modelgridindex); - assert_always(std::isfinite(R)); - // printout("rad recombination of element %d, ion %d, level %d, to lower level %d has rate %g (ne %g and Te - // %g)\n",element,ion,pkt_ptr->mastate.level,level,R/nne,nne,T_e); printout("%d %d %d %d %g %g %g - // \n",element,ion,pkt_ptr->mastate.level,level,R/nne,nne,T_e); - const double C = col_recombination_ratecoeff(modelgridindex, element, ion + 1, upper, level, epsilon_trans); - assert_always(std::isfinite(C)); - // C=C*1.e-10; - - double const upper_renorm = get_levelpop(modelgridindex, element, ion + 1, upper) / upperion_partition; - assert_always(std::isfinite(upper_renorm)); - // TODO: would the line below be correct, or is it equal to the above line? - // double upper_renorm = superlevel_boltzmann(modelgridindex,element,ion+1,upper) / upperion_partition; - rate_matrix[upper_use * (nlte_size) + upper_use] -= (R + C) * upper_renorm; - rate_matrix[level_use * (nlte_size) + upper_use] += (R + C) * upper_renorm; - } - - // balance_vector[level_use] += -1. * get_groundlevelpop(modelgridindex,element,ion+1) * (R + C); - } - } - - // replace the first line of the matrix with the normalisation constraint - printout("replace the first line of the matrix with the normalisation constraint\n"); - rate_matrix[0] = 1.0; - balance_vector[0] = ionstagepop(modelgridindex, element, ion); - for (int level = 1; level < nlte_size; level++) { - rate_matrix[level] = 1.0; - balance_vector[level] = 0.0; - } - rate_matrix[nlte_size - 1] = 0.0; - - for (int level = 0; level < nlte_size; level++) { - for (int level_use = 0; level_use < nlte_size; level_use++) { - // printout("%g ", rate_matrix[level*nlte_size + level_use]); - if (!std::isfinite(rate_matrix[level * nlte_size + level_use])) { - printout("[fatal]: NLTE matrix with non-finite element: %d %d %g\n", level, level_use, - rate_matrix[level * nlte_size + level_use]); - printout("[fatal]: found when handling element %d and ion %d\n", element, ion); - printout("[fatal]: the relevant ground state populations are %g and %g\n", - get_groundlevelpop(modelgridindex, element, ion), - get_groundlevelpop(modelgridindex, element, ion + 1)); - abort(); - } - } - // printout("\n"); - } - // printout("\n"); - - for (int level = 0; level < nlte_size; level++) { - // printout("%g ",balance_vector[level] ); - if (!std::isfinite(balance_vector[level])) { - printout("[fatal]: NLTE balance with non-finite element: %d %g\n", level, balance_vector[level]); - printout("[fatal]: found when handling element %d and ion %d\n", element, ion); - printout("[fatal]: the relevant ground state populations are %g and %g\n", - get_groundlevelpop(modelgridindex, element, ion), - get_groundlevelpop(modelgridindex, element, ion + 1)); - abort(); - } - } - // printout("\n"); - - // printout("rate %p balance %p NULL %p\n", rate_matrix, balance_vector, (void *)nullptr); - - gsl_matrix_view m = gsl_matrix_view_array(rate_matrix, nlte_size, nlte_size); - gsl_permutation *p = gsl_permutation_alloc(nlte_size); - - int s = 0; // sign of the transformation - gsl_linalg_LU_decomp(&m.matrix, p, &s); - - gsl_vector_view const b = gsl_vector_view_array(balance_vector, nlte_size); - gsl_vector *x = gsl_vector_alloc(nlte_size); - gsl_linalg_LU_solve(&m.matrix, p, &b.vector, x); // solve matrix equation m * x = b for x (populations) - // gsl_linalg_HH_solve (&m.matrix, &b.vector, x); - - // printf("after solving: rate %p balance %p NULL %p\n", rate_matrix, balance_vector, (void *)nullptr); - - printout("The ground state populations were %g, %g, %g and %g\n", get_groundlevelpop(modelgridindex, element, ion), - gsl_vector_get(x, 0), get_groundlevelpop(modelgridindex, element, ion + 1), - gsl_vector_get(x, nlte_size - 1)); - - // printout("The partition functions (and ratios to gs) were: %g (%g) %g (%g)\n", - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion]/stat_weight(element,ion,0),grid::modelgrid[modelgridindex].composition[element].partfunct[ion+1], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion+1]/stat_weight(element,ion+1,0)); - - if ((get_groundlevelpop(modelgridindex, element, ion) > (1.1 * MINPOP)) && - (gsl_vector_get(x, 0) > (1.1 * MINPOP))) { - test_ratio = get_groundlevelpop(modelgridindex, element, ion) / gsl_vector_get(x, 0); - if (test_ratio < 1) { - test_ratio = 1. / test_ratio; - } - } else { - test_ratio = 0.0; - } - - double test_ratio_upper = NAN; - if ((get_groundlevelpop(modelgridindex, element, ion + 1) > (1.1 * MINPOP)) && - (gsl_vector_get(x, nlte_size - 1) > (1.1 * MINPOP))) { - test_ratio_upper = get_groundlevelpop(modelgridindex, element, ion + 1) * - grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1] / - stat_weight(element, ion + 1, 0) / gsl_vector_get(x, nlte_size - 1); - if (test_ratio_upper < 1) { - test_ratio_upper = 1. / test_ratio_upper; - } - } else { - test_ratio_upper = 0.0; - } - - if (test_ratio_upper > test_ratio) { - test_ratio = test_ratio_upper; - } - - printout("The test ratios are %g %g. Passing %g.\n", - get_groundlevelpop(modelgridindex, element, ion) / gsl_vector_get(x, 0), - get_groundlevelpop(modelgridindex, element, ion + 1) * - grid::modelgrid[modelgridindex].composition[element].partfunct[ion + 1] / - stat_weight(element, ion + 1, 0) / gsl_vector_get(x, nlte_size - 1), - test_ratio); - // printout("The test ratio is %g. Passing %g.\n", - // get_groundlevelpop(modelgridindex,element,ion+1)*grid::modelgrid[modelgridindex].composition[element].partfunct[ion+1]/stat_weight(element,ion+1,0)/gsl_vector_get(x,nlte_size-1), - // test_ratio); - - // printout("The top five excited states were %g, %g, %g, %g and - // %g.\n",gsl_vector_get(x,nlevels_nlte-4),gsl_vector_get(x,nlevels_nlte-3),gsl_vector_get(x,nlevels_nlte-2),gsl_vector_get(x,nlevels_nlte-1),gsl_vector_get(x,nlevels_nlte)); - - // printout("The first five excited states were %g, %g, %g, %g and - // %g.\n",gsl_vector_get(x,1),gsl_vector_get(x,2),gsl_vector_get(x,3),gsl_vector_get(x,4),gsl_vector_get(x,5)); - - // lag = 0.0; - - // if ((check=(fabs((1.0 - test_ratio)/(1.0 + test_ratio))) > 0.05) && (check < 0.2)) - //{ - // lag=1.0; - // } - - // Write the NLTE level populations to the array - for (int level = 1; level < nlevels_nlte + 1; level++) { - grid::modelgrid[modelgridindex].nlte_pops[nlte_start + level - 1] = - gsl_vector_get(x, level) / grid::get_rho(modelgridindex); - // printout("I have interfered with index %d.\n", nlte_start+level-1); - // grid::modelgrid[modelgridindex].nlte_pops[nlte_start+level-1] = - // ((lag*grid::modelgrid[modelgridindex].nlte_pops[nlte_start+level-1]) + gsl_vector_get(x,level))/(lag - // + 1.0)/grid::get_rho(modelgridindex); - } - // If there is a superlevel then write that too - - if (super_level) { - // printout("I thought the super level was: %g\n", - // grid::modelgrid[modelgridindex].nlte_pops[nlte_start+nlevels_nlte]); - - grid::modelgrid[modelgridindex].nlte_pops[nlte_start + nlevels_nlte] = - gsl_vector_get(x, nlevels_nlte + 1) / grid::modelgrid[modelgridindex].rho / superlevel_partition; - // grid::modelgrid[modelgridindex].nlte_pops[nlte_start+nlevels_nlte] = - //((lag*grid::modelgrid[modelgridindex].nlte_pops[nlte_start+nlevels_nlte]) + - // gsl_vector_get(x,nlevels_nlte+1))/(lag + 1.0)/grid::modelgrid[modelgridindex].rho/superlevel_partition; - - // printout("Now I think it is: %g\n", grid::modelgrid[modelgridindex].nlte_pops[nlte_start+nlevels_nlte]); - // printout("I also interfered with index %d.\n", nlte_start+nlevels_nlte); - } - - // printout("I had a ground level pop of %g, a part fn of %g and therefore an ion pop of %g\n", - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion]*grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]/stat_weight(element,ion,0)); - - grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = gsl_vector_get(x, 0); - - // printout("Now I have a ground level pop of %g, a part fn of %g and therefore an ion pop of %g\n", - // grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion], calculate_partfunct(element, ion, - // modelgridindex), calculate_partfunct(element, ion, - // modelgridindex)*grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]/stat_weight(element,ion,0)); - - // printout("For the ion above, I had ground state of %g, a part fn of %g and therefore an ion pop of - // %g\n",grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion+1], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion+1], - // grid::modelgrid[modelgridindex].composition[element].partfunct[ion+1]*grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion+1]/stat_weight(element,ion+1,0)); - - // printout("Now I get that it should have an ion pop of %g\n", gsl_vector_get(x,nlte_size-1)); - - // printout("Accordingly, my phi-factor is: %g\n", calculate_partfunct(element, ion, - // modelgridindex)*grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion]/stat_weight(element,ion,0)/gsl_vector_get(x,nlte_size-1)/nne); - // printout("The ionization solver gives phi = %g\n", phi(element, ion, modelgridindex)); - - // printout("From ion fract, the lower and upper ion pops should be %g and %g\n", ionfract(element, ion, - // modelgridindex, nne)*nnelement, ionfract(element, ion+1, modelgridindex, nne)*nnelement); printout("I think that - // the element population is: %g (from %g and %g)\n", nnelement, grid::get_elem_abundance(modelgridindex,element), - // grid::get_rho(modelgridindex)); printout("I currently think that the top ion is: %d\n", - // elements_uppermost_ion[tid][element]); - - gsl_permutation_free(p); - // printout("I freed up p\n"); - gsl_vector_free(x); - // printout("I freed up x\n"); - // printf("rate %p balane %p NULL %p\n", rate_matrix, balance_vector,nullptr); - - free(rate_matrix); - // printout("I freed up rate_matrix\n"); - free(balance_vector); - // printout("I freed up balance_vector\n"); - } else { - // STUFF FOR "NOT USING" CASE - - nltepop_reset_element(modelgridindex, element); - test_ratio = 0.0; - } - - return test_ratio; -} - auto superlevel_boltzmann(const int modelgridindex, const int element, const int ion, const int level) -> double { const int superlevel_index = get_nlevels_nlte(element, ion) + 1; const double T_exc = LTEPOP_EXCITATION_USE_TJ ? grid::get_TJ(modelgridindex) : grid::get_Te(modelgridindex); @@ -1584,7 +1102,7 @@ void nltepop_close_file() { } void nltepop_write_to_file(const int modelgridindex, const int timestep) { - if (globals::initial_iteration) { // NLTE solver hasn't been run yet + if (globals::lte_iteration || grid::modelgrid[modelgridindex].thick == 1) { // NLTE solver hasn't been run yet return; } @@ -1639,7 +1157,7 @@ void nltepop_write_to_file(const int modelgridindex, const int timestep) { // grid::modelgrid[modelgridindex].nlte_pops[ion_first_nlte + nlevels_nlte]); } - const double ion_popfrac = nnlevelnlte / ionstagepop(modelgridindex, element, ion); + const double ion_popfrac = nnlevelnlte / get_nnion(modelgridindex, element, ion); fprintf(nlte_file, "%11.5e %11.5e %11.5e\n", nnlevellte, nnlevelnlte, ion_popfrac); } } @@ -1667,10 +1185,8 @@ void nltepop_write_restart_data(FILE *restart_file) { grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]); } } - if (NLTE_POPS_ON) { - for (int nlteindex = 0; nlteindex < globals::total_nlte_levels; nlteindex++) { - fprintf(restart_file, "%la ", grid::modelgrid[modelgridindex].nlte_pops[nlteindex]); - } + for (int nlteindex = 0; nlteindex < globals::total_nlte_levels; nlteindex++) { + fprintf(restart_file, "%la ", grid::modelgrid[modelgridindex].nlte_pops[nlteindex]); } } } @@ -1716,15 +1232,13 @@ void nltepop_read_restart_data(FILE *restart_file) { } } } - if (NLTE_POPS_ON) { - for (int nlteindex = 0; nlteindex < globals::total_nlte_levels; nlteindex++) { + for (int nlteindex = 0; nlteindex < globals::total_nlte_levels; nlteindex++) { #ifdef MPI_ON - if (globals::rank_in_node != 0) { - assert_always(fscanf(restart_file, "%*a ") == 0); // discard value (master rank of this node will set it) - } else + if (globals::rank_in_node != 0) { + assert_always(fscanf(restart_file, "%*a ") == 0); // discard value (master rank of this node will set it) + } else #endif - assert_always(fscanf(restart_file, "%la ", &grid::modelgrid[modelgridindex].nlte_pops[nlteindex]) == 1); - } + assert_always(fscanf(restart_file, "%la ", &grid::modelgrid[modelgridindex].nlte_pops[nlteindex]) == 1); } } } \ No newline at end of file diff --git a/nltepop.h b/nltepop.h index b83a4e8c6..6ab5cda90 100644 --- a/nltepop.h +++ b/nltepop.h @@ -4,7 +4,6 @@ #include void solve_nlte_pops_element(int element, int modelgridindex, int timestep, int nlte_iter); -double solve_nlte_pops_ion(int element, int ion, int modelgridindex, int timestep); double superlevel_boltzmann(int modelgridindex, int element, int ion, int level); void nltepop_write_to_file(int modelgridindex, int timestep); void nltepop_open_file(int my_rank); diff --git a/nonthermal.cc b/nonthermal.cc index 76d450656..48e0021b5 100644 --- a/nonthermal.cc +++ b/nonthermal.cc @@ -25,17 +25,17 @@ namespace nonthermal { // THESE OPTIONS ARE USED TO TEST THE SF SOLVER // Compare to Kozma & Fransson (1992) pure-oxygen plasma, nne = 1e8, x_e = 0.01 // #define yscalefactoroverride(mgi) (1e10) -// #define get_tot_nion(x) (1e10) -// #define ionstagepop(modelgridindex, element, ion) ionstagepop_override(modelgridindex, element, ion) +// #define get_nnion_tot(x) (1e10) +// #define get_nnion(modelgridindex, element, ion) get_nnion_override(modelgridindex, element, ion) // #define grid::get_nne(x) (1e8) // #define SFPTS 10000 // number of energy points in the Spencer-Fano solution vector // #define SF_EMAX 3000. // eV // #define SF_EMIN 1. // eV // -// static double ionstagepop_override(const int modelgridindex, const int element, const int ion) +// static double get_nnion_override(const int modelgridindex, const int element, const int ion) // Fake the composition to test the NT solver // { -// const double nntot = get_tot_nion(modelgridindex); +// const double nntot = get_nnion_tot(modelgridindex); // if (get_atomicnumber(element) == 8) // { // const int ion_stage = get_ionstage(element, ion); @@ -47,14 +47,13 @@ namespace nonthermal { // return 0.; // } -#define STORE_NT_SPECTRUM \ - false // if this is on, the non-thermal energy spectrum will be kept in memory for - // every grid cell during packet propagation, which - // can take up a lot of memory for large grid sizes - // alternatively, just the non-thermal ionization rates can be stored - // but we might want to re-enable this option to incorporate - // non-thermal excitation rates if there are - // many more transitions to store than there are NT spectrum samples +constexpr bool STORE_NT_SPECTRUM = false; // if this is on, the non-thermal energy spectrum will be kept in memory for + // every grid cell during packet propagation, which + // can take up a lot of memory for large grid sizes + // alternatively, just the non-thermal ionization rates can be stored + // but we might want to re-enable this option to incorporate + // non-thermal excitation rates if there are + // many more transitions to store than there are NT spectrum samples // minimum number fraction of the total population to include in SF solution constexpr double minionfraction = 1.e-8; @@ -144,38 +143,6 @@ static struct nt_solution_struct *nt_solution; static double *deposition_rate_density; static int *deposition_rate_density_timestep; -// for descending sort -static auto compare_excitation_fractions(const void *p1, const void *p2) -> int { - const auto *elem1 = static_cast(p1); - const auto *elem2 = static_cast(p2); - - if (elem1->frac_deposition < elem2->frac_deposition) { - return 1; - } - if (elem1->frac_deposition > elem2->frac_deposition) { - return -1; - } - return 0; -} - -// for ascending sort -static auto compare_excitation_lineindicies(const void *p1, const void *p2) -> int { - const auto *elem1 = static_cast(p1); - const auto *elem2 = static_cast(p2); - - if (elem1->lineindex > elem2->lineindex) { - return 1; - } - if (elem1->lineindex < elem2->lineindex) { - return -1; - } - return 0; -} - -#ifndef get_tot_nion -static auto get_tot_nion(const int modelgridindex) -> double { return get_nntot(modelgridindex); } -#endif - static void read_binding_energies() { FILE *binding = fopen_required("binding_energies.txt", "r"); @@ -346,7 +313,7 @@ static void read_auger_data() { printout("\n"); // printout("ionpot %g %g, g %d\n", colliondata[i].ionpot_ev, ionpot_ev, g); - bool const found_existing_data = (collionrow.auger_g_accumulated > 0.); + const bool found_existing_data = (collionrow.auger_g_accumulated > 0.); // keep existing data but update according to statistical weight represented by existing and new data const double oldweight = collionrow.auger_g_accumulated / (g + collionrow.auger_g_accumulated); @@ -591,7 +558,7 @@ void calculate_deposition_rate_density(const int modelgridindex, const int times { const double gamma_deposition = globals::rpkt_emiss[modelgridindex] * FOURPI; - const double tmid = globals::time_step[timestep].mid; + const double tmid = globals::timesteps[timestep].mid; const double rho = grid::get_rho(modelgridindex); // TODO: calculate thermalisation ratio from the previous timestep either globally (easy) or per cell @@ -622,13 +589,7 @@ void calculate_deposition_rate_density(const int modelgridindex, const int times auto get_deposition_rate_density(const int modelgridindex) -> double // should be in erg / s / cm^3 { - // if (deposition_rate_density[modelgridindex] <= 0) - // { - // calculate_deposition_rate_density(modelgridindex, nts_global); - // printout("No deposition_rate_density for cell %d. Calculated value of %g has been stored.\n", - // modelgridindex, deposition_rate_density[modelgridindex]); - // } - assert_always(deposition_rate_density_timestep[modelgridindex] == globals::nts_global); + assert_always(deposition_rate_density_timestep[modelgridindex] == globals::timestep); assert_always(deposition_rate_density[modelgridindex] >= 0); return deposition_rate_density[modelgridindex]; } @@ -977,7 +938,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double // not valid for energy > SF_EMIN { const double energy_ev = energy / EV; - const double tot_nion = get_tot_nion(modelgridindex); + const double tot_nion = get_nnion_tot(modelgridindex); double N_e = 0.; for (int element = 0; element < get_nelements(); element++) { @@ -987,7 +948,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double for (int ion = 0; ion < nions; ion++) { double N_e_ion = 0.; const int ionstage = get_ionstage(element, ion); - const double nnion = ionstagepop(modelgridindex, element, ion); + const double nnion = get_nnion(modelgridindex, element, ion); if (nnion < minionfraction * tot_nion) { // skip negligible ions continue; @@ -1017,7 +978,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double } // ionization terms - for (auto &collionrow : colliondata) { + for (const auto &collionrow : colliondata) { if (collionrow.Z == Z && collionrow.nelec == Z - ionstage + 1) { const double ionpot_ev = collionrow.ionpot_ev; const double J = get_J(Z, ionstage, ionpot_ev); @@ -1028,7 +989,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double // integral from ionpot up to lambda for (int i = integral1startindex; i <= integral1stopindex; i++) { - double const endash = gsl_vector_get(envec, i); + const double endash = gsl_vector_get(envec, i); const double delta_endash = DELTA_E; N_e_ion += get_y(modelgridindex, energy_ev + endash) * xs_impactionization(energy_ev + endash, collionrow) * @@ -1038,7 +999,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double // integral from 2E + I up to E_max const int integral2startindex = get_energyindex_ev_lteq(2 * energy_ev + ionpot_ev); for (int i = integral2startindex; i < SFPTS; i++) { - double const endash = gsl_vector_get(envec, i); + const double endash = gsl_vector_get(envec, i); const double delta_endash = DELTA_E; N_e_ion += get_y_sample(modelgridindex, i) * xs_impactionization(endash, collionrow) * Psecondary(endash, energy_ev + ionpot_ev, ionpot_ev, J) * delta_endash; @@ -1272,7 +1233,7 @@ static auto get_oneoverw(const int element, const int ion, const int modelgridin double Zbar = 0.0; // mass-weighted average atomic number for (int ielement = 0; ielement < get_nelements(); ielement++) { - Zbar += grid::modelgrid[modelgridindex].composition[ielement].abundance * globals::elements[ielement].anumber; + Zbar += grid::get_elem_abundance(modelgridindex, ielement) * get_atomicnumber(ielement); } // printout("cell %d has Zbar of %g\n", modelgridindex, Zbar); @@ -1288,7 +1249,7 @@ static auto calculate_nt_frac_ionization_shell(const int modelgridindex, const i const struct collionrow &collionrow) -> double // the fraction of deposition energy that goes into ionising electrons in this particular shell { - const double nnion = ionstagepop(modelgridindex, element, ion); // hopefully ions per cm^3? + const double nnion = get_nnion(modelgridindex, element, ion); // hopefully ions per cm^3? const double ionpot_ev = collionrow.ionpot_ev; gsl_vector *cross_section_vec = gsl_vector_alloc(SFPTS); @@ -1315,7 +1276,7 @@ static auto nt_ionization_ratecoeff_wfapprox(const int modelgridindex, const int // to get the non-thermal ionization rate we need to divide the energy deposited // per unit volume per unit time in the grid cell (sum of terms above) // by the total ion number density and the "work per ion pair" - return deposition_rate_density / get_tot_nion(modelgridindex) * get_oneoverw(element, ion, modelgridindex); + return deposition_rate_density / get_nnion_tot(modelgridindex) * get_oneoverw(element, ion, modelgridindex); } static auto calculate_nt_ionization_ratecoeff(const int modelgridindex, const int element, const int ion, @@ -1378,8 +1339,8 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int const int Z = get_atomicnumber(element); const int ionstage = get_ionstage(element, ion); const int uniqueionindex = get_uniqueionindex(element, ion); - const double nnion = ionstagepop(modelgridindex, element, ion); // ions/cm^3 - const double tot_nion = get_tot_nion(modelgridindex); + const double nnion = get_nnion(modelgridindex, element, ion); // ions/cm^3 + const double tot_nion = get_nnion_tot(modelgridindex); const double X_ion = nnion / tot_nion; // molar fraction of this ion // The ionization rates of all shells of an ion add to make the ion's total ionization rate, @@ -1498,7 +1459,7 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int } } -static auto get_eff_ionpot(const int modelgridindex, const int element, int const ion) -> float +static auto get_eff_ionpot(const int modelgridindex, const int element, const int ion) -> float // get the effective ion potential from the stored value // a value of 0. should be treated as invalid { @@ -1517,7 +1478,7 @@ static auto nt_ionization_ratecoeff_sf(const int modelgridindex, const int eleme const double deposition_rate_density = get_deposition_rate_density(modelgridindex); if (deposition_rate_density > 0.) { - return deposition_rate_density / get_tot_nion(modelgridindex) / get_eff_ionpot(modelgridindex, element, ion); + return deposition_rate_density / get_nnion_tot(modelgridindex) / get_eff_ionpot(modelgridindex, element, ion); // alternatively, if the y vector is still in memory: // return calculate_nt_ionization_ratecoeff(modelgridindex, element, ion); } @@ -1626,7 +1587,7 @@ auto nt_ionization_ratecoeff(const int modelgridindex, const int element, const assert_always(grid::get_numassociatedcells(modelgridindex) > 0); if (NT_SOLVE_SPENCERFANO) { - double const Y_nt = nt_ionization_ratecoeff_sf(modelgridindex, element, ion); + const double Y_nt = nt_ionization_ratecoeff_sf(modelgridindex, element, ion); if (!std::isfinite(Y_nt)) { // probably because eff_ionpot = 0 because the solver hasn't been run yet, or no impact ionization cross sections // exist @@ -1711,7 +1672,7 @@ auto nt_excitation_ratecoeff(const int modelgridindex, const int element, const // binary search, assuming the excitation list is sorted by lineindex ascending auto ntexclist = nt_solution[modelgridindex].frac_excitations_list; - auto ntexcitation = std::lower_bound(ntexclist.begin(), ntexclist.end(), lineindex, + auto ntexcitation = std::lower_bound(ntexclist.cbegin(), ntexclist.cend(), lineindex, [](const auto &exc, const int lineindex) { return exc.lineindex < lineindex; }); if (ntexcitation == ntexclist.end() || ntexcitation->lineindex != lineindex) { return 0.; @@ -1745,7 +1706,7 @@ static void select_nt_ionization(int modelgridindex, int *element, int *lowerion static auto ion_ntion_energyrate(int modelgridindex, int element, int lowerion) -> double { // returns the energy rate [erg/s] going toward non-thermal ionisation of lowerion - const double nnlowerion = ionstagepop(modelgridindex, element, lowerion); + const double nnlowerion = get_nnion(modelgridindex, element, lowerion); double enrate = 0.; for (int upperion = lowerion + 1; upperion <= nt_ionisation_maxupperion(element, lowerion); upperion++) { const double upperionprobfrac = @@ -1815,9 +1776,6 @@ void do_ntlepton(struct packet *pkt_ptr) { // printout("frac_ionization compare %g and %g\n", frac_ionization, get_nt_frac_ionization(modelgridindex)); // const double frac_ionization = 0.; - // const double frac_excitation = get_nt_frac_excitation(modelgridindex); - const double frac_excitation = 0.; - if (zrand < frac_ionization) { int element = -1; int lowerion = -1; @@ -1854,7 +1812,10 @@ void do_ntlepton(struct packet *pkt_ptr) { return; } - if (NT_EXCITATION_ON && zrand < frac_ionization + frac_excitation) { + + // const double frac_excitation = NT_EXCITATION_ON ? get_nt_frac_excitation(modelgridindex) : 0; + const double frac_excitation = 0.; + if (zrand < (frac_ionization + frac_excitation)) { zrand -= frac_ionization; // now zrand is between zero and frac_excitation // the selection algorithm is the same as for the ionization transitions @@ -1901,7 +1862,7 @@ void do_ntlepton(struct packet *pkt_ptr) { static void analyse_sf_solution(const int modelgridindex, const int timestep, const bool enable_sfexcitation) { const float nne = grid::get_nne(modelgridindex); - const double nntot = get_tot_nion(modelgridindex); + const double nntot = get_nnion_tot(modelgridindex); const double nnetot = grid::get_nnetot(modelgridindex); double frac_excitation_total = 0.; @@ -1916,9 +1877,9 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co const int uniqueionindex = get_uniqueionindex(element, ion); const int ionstage = get_ionstage(element, ion); - const double nnion = ionstagepop(modelgridindex, element, ion); + const double nnion = get_nnion(modelgridindex, element, ion); - // if (nnion < minionfraction * get_tot_nion(modelgridindex)) // skip negligible ions + // if (nnion < minionfraction * get_nnion_tot(modelgridindex)) // skip negligible ions if (nnion <= 0.) { // skip zero-abundance ions continue; } @@ -1968,7 +1929,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co if (!enable_sfexcitation) { nlevels = -1; // disable all excitations } - const bool above_minionfraction = (nnion >= minionfraction * get_tot_nion(modelgridindex)); + const bool above_minionfraction = (nnion >= minionfraction * get_nnion_tot(modelgridindex)); for (int lower = 0; lower < nlevels; lower++) { const double statweight_lower = stat_weight(element, ion, lower); @@ -2067,9 +2028,10 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co } if constexpr (NT_EXCITATION_ON && (MAX_NT_EXCITATIONS_STORED > 0)) { - qsort(nt_solution[modelgridindex].frac_excitations_list.data(), - nt_solution[modelgridindex].frac_excitations_list.size(), sizeof(struct nt_excitation_struct), - compare_excitation_fractions); + // sort by descending frac_deposition + std::sort(nt_solution[modelgridindex].frac_excitations_list.begin(), + nt_solution[modelgridindex].frac_excitations_list.end(), + [](const auto &a, const auto &b) { return static_cast(a.frac_deposition > b.frac_deposition); }); // the excitation list is now sorted by frac_deposition descending const double deposition_rate_density = get_deposition_rate_density(modelgridindex); @@ -2106,7 +2068,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co nt_solution[modelgridindex].frac_excitations_list[excitationindex].ratecoeffperdeposition; const double ntcollexc_ratecoeff = ratecoeffperdeposition * deposition_rate_density; - const double t_mid = globals::time_step[timestep].mid; + const double t_mid = globals::timesteps[timestep].mid; const double radexc_ratecoeff = rad_excitation_ratecoeff(modelgridindex, element, ion, lower, uptransindex, epsilon_trans, lineindex, t_mid); @@ -2124,10 +2086,10 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co } } - // now sort the excitation list by lineindex ascending for fast lookup with a binary search - qsort(nt_solution[modelgridindex].frac_excitations_list.data(), - nt_solution[modelgridindex].frac_excitations_list.size(), sizeof(struct nt_excitation_struct), - compare_excitation_lineindicies); + // sort the excitation list by ascending lineindex for fast lookup with a binary search + std::sort(nt_solution[modelgridindex].frac_excitations_list.begin(), + nt_solution[modelgridindex].frac_excitations_list.end(), + [](const auto &a, const auto &b) { return static_cast(a.lineindex < b.lineindex); }); } // NT_EXCITATION_ON @@ -2166,7 +2128,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co printout(" (replacing calculated frac_heating_tot with %g to make frac_sum = 1.0)\n", nt_solution[modelgridindex].frac_heating); - // const double nnion = ionstagepop(modelgridindex, element, ion); + // const double nnion = get_nnion(modelgridindex, element, ion); // double ntexcit_in_a = 0.; // for (int level = 0; level < get_nlevels(0, 1); level++) // { @@ -2440,7 +2402,7 @@ void solve_spencerfano(const int modelgridindex, const int timestep, const int i const float nne = grid::get_nne(modelgridindex); // electrons per cm^3 // const double nnetot = grid::get_nnetot(modelgridindex); - const double nne_per_ion = nne / get_tot_nion(modelgridindex); + const double nne_per_ion = nne / get_nnion_tot(modelgridindex); const double nne_per_ion_last = nt_solution[modelgridindex].nneperion_when_solved; const double nne_per_ion_fracdiff = fabs((nne_per_ion_last / nne_per_ion) - 1.); const int timestep_last_solved = nt_solution[modelgridindex].timestep_last_solved; @@ -2471,8 +2433,8 @@ void solve_spencerfano(const int modelgridindex, const int timestep, const int i nt_solution[modelgridindex].nneperion_when_solved = nne_per_ion; nt_solution[modelgridindex].timestep_last_solved = timestep; - bool const enable_sfexcitation = true; - bool const enable_sfionization = true; + const bool enable_sfexcitation = true; + const bool enable_sfionization = true; // if (timestep <= globals::num_lte_timesteps) // { // // for the first run of the solver at the first NLTE timestep (which usually requires many iterations), @@ -2522,9 +2484,9 @@ void solve_spencerfano(const int modelgridindex, const int timestep, const int i const int nions = get_nions(element); bool first_included_ion_of_element = true; for (int ion = 0; ion < nions; ion++) { - const double nnion = ionstagepop(modelgridindex, element, ion); // hopefully ions per cm^3? + const double nnion = get_nnion(modelgridindex, element, ion); // hopefully ions per cm^3? - if (nnion < minionfraction * get_tot_nion(modelgridindex)) // skip negligible ions + if (nnion < minionfraction * get_nnion_tot(modelgridindex)) // skip negligible ions { continue; } diff --git a/packet.cc b/packet.cc index 71de54a76..8312e7fd2 100644 --- a/packet.cc +++ b/packet.cc @@ -23,26 +23,46 @@ static void place_pellet(const double e0, const int cellindex, const int pktnumb pkt_ptr->where = cellindex; pkt_ptr->number = pktnumber; /// record the packets number for debugging pkt_ptr->prop_time = globals::tmin; - // pkt_ptr->last_cross = NONE; + // pkt_ptr->last_cross = BOUNDARY_NONE; pkt_ptr->originated_from_particlenotgamma = false; - if (GRID_TYPE == GRID_SPHERICAL1D) { - const double zrand3 = rng_uniform(); + if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { + const double zrand = rng_uniform(); const double r_inner = grid::get_cellcoordmin(cellindex, 0); - const double r_outer = grid::get_cellcoordmin(cellindex, 0) + grid::wid_init(cellindex); - const double radius = pow(zrand3 * pow(r_inner, 3) + (1. - zrand3) * pow(r_outer, 3), 1 / 3.); + const double r_outer = grid::get_cellcoordmax(cellindex, 0); + // use equal volume probability distribution to select radius + const double radius = pow(zrand * pow(r_inner, 3) + (1. - zrand) * pow(r_outer, 3), 1 / 3.); // assert_always(radius >= r_inner); // assert_always(radius <= r_outer); get_rand_isotropic_unitvec(pkt_ptr->pos); vec_scale(pkt_ptr->pos, radius); - } else { + + } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { + const double zrand1 = rng_uniform(); + const double rcyl_inner = grid::get_cellcoordmin(cellindex, 0); + const double rcyl_outer = grid::get_cellcoordmax(cellindex, 0); + // use equal area probability distribution to select radius + const double rcyl_rand = sqrt(zrand1 * pow(rcyl_inner, 2) + (1. - zrand1) * pow(rcyl_outer, 2)); + const double theta_rand = rng_uniform() * 2 * PI; + pkt_ptr->pos[0] = std::cos(theta_rand) * rcyl_rand; + pkt_ptr->pos[1] = std::sin(theta_rand) * rcyl_rand; + + const double zrand2 = rng_uniform_pos(); + pkt_ptr->pos[2] = grid::get_cellcoordmin(cellindex, 1) + (zrand2 * grid::wid_init(cellindex, 1)); + + } else if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { for (int axis = 0; axis < 3; axis++) { const double zrand = rng_uniform_pos(); - pkt_ptr->pos[axis] = grid::get_cellcoordmin(cellindex, axis) + (zrand * grid::wid_init(0)); + pkt_ptr->pos[axis] = grid::get_cellcoordmin(cellindex, axis) + (zrand * grid::wid_init(cellindex, axis)); } + } else { + assert_always(false); } + // ensure that the random position was inside the cell we selected + assert_always(grid::get_cellindex_from_pos(pkt_ptr->pos, pkt_ptr->prop_time) == cellindex); + const int mgi = grid::get_cell_modelgridindex(cellindex); decay::setup_radioactive_pellet(e0, mgi, pkt_ptr); @@ -52,7 +72,7 @@ static void place_pellet(const double e0, const int cellindex, const int pktnumb // pellet packet is moving with the homologous flow, so dir is proportional to pos vec_norm(pkt_ptr->pos, pkt_ptr->dir); // assign dir = pos / vec_len(pos) - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; pkt_ptr->trueemissiontype = EMTYPE_NOTSET; @@ -118,9 +138,9 @@ void packet_init(struct packet *pkt) const double targetval = zrand * norm; // first en_cumulative[i] such that en_cumulative[i] > targetval - auto upperval = std::upper_bound(en_cumulative.begin(), en_cumulative.end(), targetval); + auto upperval = std::upper_bound(en_cumulative.cbegin(), en_cumulative.cend(), targetval); assert_always(upperval != en_cumulative.end()); - const ptrdiff_t cellindex = std::distance(en_cumulative.begin(), upperval); + const ptrdiff_t cellindex = std::distance(en_cumulative.cbegin(), upperval); place_pellet(e0, cellindex, n, &pkt[n]); } diff --git a/packet.h b/packet.h index c5c56e083..4c282b833 100644 --- a/packet.h +++ b/packet.h @@ -19,8 +19,6 @@ enum packet_type { constexpr int EMTYPE_NOTSET = -9999000; constexpr int EMTYPE_FREEFREE = -9999999; -#include "boundary.h" - struct mastate { int element; /// macro atom of type element (this is an element index) int ion; /// in ionstage ion (this is an ion index) @@ -28,15 +26,25 @@ struct mastate { int activatingline; /// Linelistindex of the activating line for bb activated MAs, -99 else. }; +enum cell_boundary { + COORD0_MIN = 101, + COORD0_MAX = 102, + COORD1_MIN = 103, + COORD1_MAX = 104, + COORD2_MIN = 105, + COORD2_MAX = 106, + BOUNDARY_NONE = 107, +}; + struct packet { - int where; // The propagation grid cell that the packet is in. - enum packet_type type; // type of packet (k-, r-, etc.) - enum cell_boundary last_cross; // To avoid rounding errors on cell crossing. - int interactions; // number of interactions the packet undergone - int nscatterings; // records number of electron scatterings a r-pkt undergone since it was emitted + int where = -1; // The propagation grid cell that the packet is in. + enum packet_type type; // type of packet (k-, r-, etc.) + enum cell_boundary last_cross = BOUNDARY_NONE; // To avoid rounding errors on cell crossing. + int interactions = 0; // number of interactions the packet undergone + int nscatterings = 0; // records number of electron scatterings a r-pkt undergone since it was emitted int last_event; // debug: stores information about the packets history - double pos[3]; // Position of the packet (x,y,z). - double dir[3]; // Direction of propagation. (x,y,z). Always a unit vector. + double pos[3] = {0}; // Position of the packet (x,y,z). + double dir[3] = {0}; // Direction of propagation. (x,y,z). Always a unit vector. double e_cmf; // The energy the packet carries in the co-moving frame. double e_rf; // The energy the packet carries in the rest frame. double nu_cmf; // The frequency in the co-moving frame. @@ -45,30 +53,30 @@ struct packet { // its linelist index (to overcome numerical problems in propagating the rpkts). int emissiontype = EMTYPE_NOTSET; // records how the packet was emitted if it is a r-pkt double em_pos[3]; // Position of the last emission (x,y,z). - float em_time; - double prop_time; // internal clock to track how far in time the packet has been propagated - int absorptiontype; // records linelistindex of the last absorption - // negative values give ff-abs (-1), bf-abs (-2), compton scattering of gammas (-3), - // photoelectric effect of gammas (-4), pair production of gammas (-5) - // decaying pellets of the 52Fe chain (-6) and pellets which decayed before the - // onset of the simulation (-7) - // decay of a positron pellet (-10) + float em_time = -1.; + double prop_time = -1.; // internal clock to track how far in time the packet has been propagated + int absorptiontype; // records linelistindex of the last absorption + // negative values give ff-abs (-1), bf-abs (-2), compton scattering of gammas (-3), + // photoelectric effect of gammas (-4), pair production of gammas (-5) + // decaying pellets of the 52Fe chain (-6) and pellets which decayed before the + // onset of the simulation (-7) + // decay of a positron pellet (-10) int trueemissiontype = EMTYPE_NOTSET; // emission type coming from a kpkt to rpkt (last thermal emission) - float trueem_time; // first thermal emission time [s] + float trueem_time = -1.; // first thermal emission time [s] double absorptionfreq; // records nu_rf of packet at last absorption - double absorptiondir[3]; // Direction of propagation (x,y,z) when a packet was last absorbed in a line. Always a - // unit vector. - double stokes[3]; // I, Q and U Stokes parameters - double pol_dir[3]; // unit vector which defines the coordinate system against which Q and U are measured; should - // always be perpendicular to dir - double tdecay; // Time at which pellet decays - enum packet_type escape_type; // Flag to tell us in which form it escaped from the grid. - float escape_time; // time at which is passes out of the grid [s] - int number; // A unique number to identify the packet + double absorptiondir[3] = {0.}; // Direction of propagation (x,y,z) when a packet was last absorbed in a line. Always + // a unit vector. + double stokes[3] = {0.}; // I, Q and U Stokes parameters + double pol_dir[3] = {0.}; // unit vector which defines the coordinate system against which Q and U are measured; + // should always be perpendicular to dir + double tdecay = -1.; // Time at which pellet decays + enum packet_type escape_type; // Flag to tell us in which form it escaped from the grid. + float escape_time = -1; // time at which is passes out of the grid [s] + int number = -1; // A unique number to identify the packet bool originated_from_particlenotgamma; // first-non-pellet packet type was gamma - int pellet_decaytype; // index into decay::decaytypes - int pellet_nucindex; // nuclide index of the decaying species - float trueemissionvelocity; + int pellet_decaytype = -1; // index into decay::decaytypes + int pellet_nucindex = -1; // nuclide index of the decaying species + float trueemissionvelocity = -1; struct mastate mastate; inline bool operator==(const packet &rhs) { diff --git a/radfield.cc b/radfield.cc index 972ab5dfd..bb117a7fc 100644 --- a/radfield.cc +++ b/radfield.cc @@ -1,5 +1,6 @@ #include "radfield.h" +#include #include #include #include @@ -7,6 +8,7 @@ #include #include #include +#include #include "atomic.h" #include "grid.h" @@ -17,7 +19,7 @@ namespace radfield { -static double *J_normfactor = nullptr; +static std::vector J_normfactor; // typedef enum // { @@ -51,8 +53,8 @@ MPI_Win win_prev_bfrate_normed = MPI_WIN_NULL; // ** Detailed lines - Jblue_lu estimators for selected lines struct Jb_lu_estimator { - double value; - int contribcount; + double value = 0.; + int contribcount = 0; }; // reallocate the detailed line arrays in units of BLOCKSIZEJBLUE @@ -72,16 +74,16 @@ static double *bfrate_raw = nullptr; // unnormalised estimators for the // expensive debugging mode to track the contributions to each bound-free rate estimator -static double *J = nullptr; // after normalisation: [ergs/s/sr/cm2/Hz] +static std::vector J; // after normalisation: [ergs/s/sr/cm2/Hz] #ifdef DO_TITER -static double *J_reduced_save = nullptr; +static std::vector J_reduced_save; #endif // J and nuJ are accumulated and then normalised in-place // i.e. be sure the normalisation has been applied (exactly once) before using the values here! -static double *nuJ = nullptr; +static std::vector nuJ; #ifdef DO_TITER -static double *nuJ_reduced_save = nullptr; +static std::vector nuJ_reduced_save; #endif using enum_prefactor = enum { @@ -214,18 +216,18 @@ void init(int my_rank, int ndo_nonempty) { const int nonempty_npts_model = grid::get_nonempty_npts_model(); - assert_always(J_normfactor == nullptr); - J_normfactor = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(double))); - J = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(double))); + J_normfactor.resize(nonempty_npts_model + 1); + J.resize(nonempty_npts_model + 1); + #ifdef DO_TITER - J_reduced_save = (double *)malloc((grid::get_npts_model() + 1) * sizeof(double)); + J_reduced_save.resize(nonempty_npts_model + 1); #endif // J and nuJ are accumulated and then normalised in-place // i.e. be sure the normalisation has been applied (exactly once) before using the values here! - nuJ = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(double))); + nuJ.resize(nonempty_npts_model + 1); #ifdef DO_TITER - nuJ_reduced_save = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(double))); + nuJ.resize(nonempty_npts_model + 1); #endif prev_Jb_lu_normed = @@ -396,11 +398,10 @@ void init(int my_rank, int ndo_nonempty) /// Initialise estimator arrays which hold the last time steps values (used to damp out /// fluctuations over timestep iterations if DO_TITER is defined) to -1. void initialise_prev_titer_photoionestimators() { - // for (n = 0; n < ngrid; n++) for (int n = 0; n < grid::get_npts_model(); n++) { -// double T_e = grid::get_Te(n); #ifdef DO_TITER - J_reduced_save[n] = -1.; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + J_reduced_save[nonemptymgi] = -1.; #endif #ifdef DO_TITER nuJ_reduced_save[n] = -1.; @@ -411,10 +412,9 @@ void initialise_prev_titer_photoionestimators() { const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { #ifdef DO_TITER - globals::gammaestimator_save[n * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = -1.; + globals::gammaestimator_save[get_ionestimindex(n, element, ion)] = -1.; if constexpr (USE_LUT_BFHEATING) { - globals::bfheatingestimator_save[n * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = - -1.; + globals::bfheatingestimator_save[get_ionestimindex(n, element, ion)] = -1.; } #endif } @@ -441,7 +441,7 @@ auto get_Jblueindex(const int lineindex) -> int int low = 0; int high = detailed_linecount; while (low <= high) { - int const mid = low + ((high - low) / 2); + const int mid = low + ((high - low) / 2); if (detailed_lineindicies[mid] < lineindex) { low = mid + 1; } else if (detailed_lineindicies[mid] > lineindex) { @@ -476,21 +476,23 @@ auto get_Jb_lu_contribcount(const int modelgridindex, const int jblueindex) -> i static auto get_bin_J(int modelgridindex, int binindex) -> double // get the normalised J_nu { - assert_testmodeonly(J_normfactor[modelgridindex] > 0.0); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_testmodeonly(J_normfactor[nonemptymgi] > 0.0); assert_testmodeonly(modelgridindex < grid::get_npts_model()); assert_testmodeonly(binindex >= 0); assert_testmodeonly(binindex < RADFIELDBINCOUNT); - const int mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; - return radfieldbins[mgibinindex].J_raw * J_normfactor[modelgridindex]; + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + return radfieldbins[mgibinindex].J_raw * J_normfactor[nonemptymgi]; } static auto get_bin_nuJ(int modelgridindex, int binindex) -> double { - assert_testmodeonly(J_normfactor[modelgridindex] > 0.0); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_testmodeonly(J_normfactor[nonemptymgi] > 0.0); assert_testmodeonly(modelgridindex < grid::get_npts_model()); assert_testmodeonly(binindex >= 0); assert_testmodeonly(binindex < RADFIELDBINCOUNT); - const int mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; - return radfieldbins[mgibinindex].nuJ_raw * J_normfactor[modelgridindex]; + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + return radfieldbins[mgibinindex].nuJ_raw * J_normfactor[nonemptymgi]; } static inline auto get_bin_nu_bar(int modelgridindex, int binindex) -> double @@ -541,7 +543,7 @@ static inline auto select_bin(double nu) -> int { void write_to_file(int modelgridindex, int timestep) { assert_always(MULTIBIN_RADFIELD_MODEL_ON); - + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); #ifdef _OPENMP #pragma omp critical(out_file) { @@ -562,7 +564,7 @@ void write_to_file(int modelgridindex, int timestep) { double J_nu_bar = 0.0; int contribcount = 0; - bool const skipoutput = false; + const bool skipoutput = false; if (binindex >= 0) { nu_lower = get_bin_nu_lower(binindex); @@ -574,8 +576,8 @@ void write_to_file(int modelgridindex, int timestep) { J_nu_bar = J_out / (nu_upper - nu_lower); contribcount = get_bin_contribcount(modelgridindex, binindex); } else if (binindex == -1) { // bin -1 is the full spectrum fit - nuJ_out = nuJ[modelgridindex]; - J_out = J[modelgridindex]; + nuJ_out = nuJ[nonemptymgi]; + J_out = J[nonemptymgi]; T_R = grid::get_TR(modelgridindex); W = grid::get_W(modelgridindex); contribcount = totalcontribs; @@ -647,10 +649,15 @@ void zero_estimators(int modelgridindex) // set up the new bins and clear the estimators in preparation // for a timestep { + if (grid::get_numassociatedcells(modelgridindex) == 0) { + return; + } + + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + if constexpr (DETAILED_BF_ESTIMATORS_ON) { assert_always(bfrate_raw != nullptr); if (grid::get_numassociatedcells(modelgridindex) > 0) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); for (int i = 0; i < globals::nbfcontinua; i++) { bfrate_raw[nonemptymgi * globals::nbfcontinua + i] = 0.; } @@ -666,12 +673,10 @@ void zero_estimators(int modelgridindex) } } - assert_always(J != nullptr); - J[modelgridindex] = 0.; // this is required even if FORCE_LTE is on - assert_always(nuJ != nullptr); - nuJ[modelgridindex] = 0.; + J[nonemptymgi] = 0.; // this is required even if FORCE_LTE is on + nuJ[nonemptymgi] = 0.; - if (MULTIBIN_RADFIELD_MODEL_ON && (grid::get_numassociatedcells(modelgridindex) > 0)) { + if (MULTIBIN_RADFIELD_MODEL_ON) { // printout("radfield: zeroing estimators in %d bins in cell %d\n",RADFIELDBINCOUNT,modelgridindex); assert_always(radfieldbins != nullptr); @@ -685,7 +690,7 @@ void zero_estimators(int modelgridindex) set_J_normfactor(modelgridindex, -1.0); } -static void update_bfestimators(const int modelgridindex, const double distance_e_cmf, const double nu_cmf, +static void update_bfestimators(const int nonemptymgi, const double distance_e_cmf, const double nu_cmf, const struct packet *const pkt_ptr) { assert_testmodeonly(DETAILED_BF_ESTIMATORS_ON); assert_always(bfrate_raw != nullptr); @@ -695,8 +700,7 @@ static void update_bfestimators(const int modelgridindex, const double distance_ } const int nbfcontinua = globals::nbfcontinua; - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); // const double dopplerfactor = 1.; const int tid = get_thread_num(); @@ -719,35 +723,20 @@ static void update_bfestimators(const int modelgridindex, const double distance_ void update_estimators(const int modelgridindex, const double distance_e_cmf, const double nu_cmf, const struct packet *const pkt_ptr) { - safeadd(J[modelgridindex], distance_e_cmf); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - safeadd(nuJ[modelgridindex], distance_e_cmf * nu_cmf); + safeadd(J[nonemptymgi], distance_e_cmf); + safeadd(nuJ[nonemptymgi], distance_e_cmf * nu_cmf); if constexpr (DETAILED_BF_ESTIMATORS_ON) { - update_bfestimators(modelgridindex, distance_e_cmf, nu_cmf, pkt_ptr); + update_bfestimators(nonemptymgi, distance_e_cmf, nu_cmf, pkt_ptr); } if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - // int binindex = 0; - // if (nu_cmf <= get_bin_nu_lower(modelgridindex,binindex)) - // { - // printout("radfield: Extending nu_lower_first from %g down to %g\n",nu_lower_first,nu_cmf); - // nu_lower_first = nu_cmf; - // } - // else if (nu_cmf > radfieldbin_nu_upper[modelgridindex][RADFIELDBINCOUNT - 1]) - // { - // binindex = RADFIELDBINCOUNT - 1; - // printout("radfield: Extending nu_upper_last from %g up to - // %g\n",get_bin_nu_upper(modelgridindex,binindex),nu_cmf); get_bin_nu_upper(modelgridindex, binindex) = nu_cmf; - // } - // else - // { - // binindex = select_bin(modelgridindex,nu_cmf); - // } const int binindex = select_bin(nu_cmf); if (binindex >= 0) { - const int mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; safeadd(radfieldbins[mgibinindex].J_raw, distance_e_cmf); safeadd(radfieldbins[mgibinindex].nuJ_raw, distance_e_cmf * nu_cmf); safeincrement(radfieldbins[mgibinindex].contribcount); @@ -763,7 +752,7 @@ void update_estimators(const int modelgridindex, const double distance_e_cmf, co } void update_lineestimator(const int modelgridindex, const int lineindex, const double increment) { - if (!DETAILED_LINE_ESTIMATORS_ON) { + if constexpr (!DETAILED_LINE_ESTIMATORS_ON) { return; } @@ -778,36 +767,18 @@ void update_lineestimator(const int modelgridindex, const int lineindex, const d } } -auto dbb_mgi(double nu, int modelgridindex) -> double { - const float T_R_fullspec = grid::get_TR(modelgridindex); - const float W_fullspec = grid::get_W(modelgridindex); - return dbb(nu, T_R_fullspec, W_fullspec); -} - auto radfield(double nu, int modelgridindex) -> double // returns mean intensity J_nu [ergs/s/sr/cm2/Hz] { if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - if (globals::nts_global >= FIRST_NLTE_RADFIELD_TIMESTEP) { - // const double lambda = 1e8 * CLIGHT / nu; - // if (lambda < 1085) // Fe II ground state edge - // { - // return dbb(nu, grid::get_TR(modelgridindex), grid::get_W(modelgridindex)); - // } + if (globals::timestep >= FIRST_NLTE_RADFIELD_TIMESTEP) { const int binindex = select_bin(nu); if (binindex >= 0) { const int mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; const struct radfieldbin_solution *const bin = &radfieldbin_solutions[mgibinindex]; if (bin->W >= 0.) { - // if (bin->fit_type == FIT_DILUTE_BLACKBODY) - { - const double J_nu = dbb(nu, bin->T_R, bin->W); - return J_nu; - } - // else - // { - // return bin->W; - // } + const double J_nu = dbb(nu, bin->T_R, bin->W); + return J_nu; } } else { // binindex < 0 // if (nu > get_bin_nu_upper(RADFIELDBINCOUNT - 1)) @@ -856,7 +827,7 @@ static auto planck_integral(double T_R, double nu_lower, double nu_upper, enum_p const gsl_function F_planck = {.function = &gsl_integrand_planck, .params = &intparas}; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); - int const status = gsl_integration_qag(&F_planck, nu_lower, nu_upper, epsabs, epsrel, GSLWSIZE, GSL_INTEG_GAUSS61, + const int status = gsl_integration_qag(&F_planck, nu_lower, nu_upper, epsabs, epsrel, GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &integral, &error); if (status != 0) { printout("planck_integral integrator status %d, GSL_FAILURE= %d. Integral value %g, setting to zero.\n", status, @@ -872,31 +843,31 @@ static auto planck_integral_analytic(double T_R, double nu_lower, double nu_uppe double integral = 0.; if (prefactor == TIMES_NU) { - double const debye_upper = gsl_sf_debye_4(HOVERKB * nu_upper / T_R) * pow(nu_upper, 4); - double const debye_lower = gsl_sf_debye_4(HOVERKB * nu_lower / T_R) * pow(nu_lower, 4); + const double debye_upper = gsl_sf_debye_4(HOVERKB * nu_upper / T_R) * pow(nu_upper, 4); + const double debye_lower = gsl_sf_debye_4(HOVERKB * nu_lower / T_R) * pow(nu_lower, 4); integral = TWOHOVERCLIGHTSQUARED * (debye_upper - debye_lower) * T_R / HOVERKB / 4.; } else { - double const debye_upper = gsl_sf_debye_3(HOVERKB * nu_upper / T_R) * pow(nu_upper, 3); - double const debye_lower = gsl_sf_debye_3(HOVERKB * nu_lower / T_R) * pow(nu_lower, 3); + const double debye_upper = gsl_sf_debye_3(HOVERKB * nu_upper / T_R) * pow(nu_upper, 3); + const double debye_lower = gsl_sf_debye_3(HOVERKB * nu_lower / T_R) * pow(nu_lower, 3); integral = TWOHOVERCLIGHTSQUARED * (debye_upper - debye_lower) * T_R / HOVERKB / 3.; if (integral == 0.) { - /*double upperexp = exp(HOVERKB * nu_upper / T_R); - double upperint = - pow(nu_upper,4) / 4 - + pow(nu_upper,3) * log(1 - upperexp) / HOVERKB - + 3 * pow(nu_upper,2) * polylog(2,upperexp) / pow(HOVERKB,2) - - 6 * nu_upper * polylog(3,upperexp) / pow(HOVERKB,3) - + 6 * polylog(4,upperexp) / pow(HOVERKB,4); - double lowerexp = exp(HOVERKB * nu_lower / T_R); - double lowerint = - pow(nu_lower,4) / 4 - + pow(nu_lower,3) * log(1 - lowerexp) / HOVERKB - + 3 * pow(nu_lower,2) * polylog(2,lowerexp) / pow(HOVERKB,2) - - 6 * nu_lower * polylog(3,lowerexp) / pow(HOVERKB,3) - + 6 * polylog(4,lowerexp) / pow(HOVERKB,4); - double integral2 = TWOHOVERCLIGHTSQUARED * (upperint - lowerint); - - printout("planck_integral_analytic is zero. debye_upper %g debye_lower %g. Test alternative %g\n", - debye_upper,debye_lower,integral2);*/ + // double upperexp = exp(HOVERKB * nu_upper / T_R); + // double upperint = - pow(nu_upper,4) / 4 + // + pow(nu_upper,3) * log(1 - upperexp) / HOVERKB + // + 3 * pow(nu_upper,2) * polylog(2,upperexp) / pow(HOVERKB,2) + // - 6 * nu_upper * polylog(3,upperexp) / pow(HOVERKB,3) + // + 6 * polylog(4,upperexp) / pow(HOVERKB,4); + // double lowerexp = exp(HOVERKB * nu_lower / T_R); + // double lowerint = - pow(nu_lower,4) / 4 + // + pow(nu_lower,3) * log(1 - lowerexp) / HOVERKB + // + 3 * pow(nu_lower,2) * polylog(2,lowerexp) / pow(HOVERKB,2) + // - 6 * nu_lower * polylog(3,lowerexp) / pow(HOVERKB,3) + // + 6 * polylog(4,lowerexp) / pow(HOVERKB,4); + // double integral2 = TWOHOVERCLIGHTSQUARED * (upperint - lowerint); + + // printout("planck_integral_analytic is zero. debye_upper %g debye_lower %g. Test alternative %g\n", + // debye_upper,debye_lower,integral2); } } @@ -904,7 +875,7 @@ static auto planck_integral_analytic(double T_R, double nu_lower, double nu_uppe } static auto delta_nu_bar(double T_R, void *paras) -> double -// difference between the average nu and the average nu of a planck function +// difference between the average nu and the average nu of a Planck function // at temperature T_R, in the frequency range corresponding to a bin { const int modelgridindex = (static_cast(paras))->modelgridindex; @@ -944,10 +915,6 @@ static auto delta_nu_bar(double T_R, void *paras) -> double delta_nu_bar, nu_bar_planck_T_R, nu_times_planck_numerical, planck_integral_numerical, nu_bar_estimator); } - // double delta_nu_bar = nu_bar_planck_T_R / nu_bar_estimator - 1.0; - - // printout("delta_nu_bar %g nu_bar_planck %g\n",delta_nu_bar,nu_bar_planck); - return delta_nu_bar; } @@ -1021,12 +988,13 @@ static auto find_T_R(int modelgridindex, int binindex) -> float { } static void set_params_fullspec(const int modelgridindex, const int timestep) { - const double nubar = nuJ[modelgridindex] / J[modelgridindex]; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const double nubar = nuJ[nonemptymgi] / J[nonemptymgi]; if (!std::isfinite(nubar) || nubar == 0.) { printout("[warning] T_R estimator infinite in cell %d, keep T_R, T_J, W of last timestep. J = %g. nuJ = %g\n", - modelgridindex, J[modelgridindex], nuJ[modelgridindex]); + modelgridindex, J[nonemptymgi], nuJ[nonemptymgi]); } else { - float T_J = pow(J[modelgridindex] * PI / STEBO, 1 / 4.); + float T_J = pow(J[nonemptymgi] * PI / STEBO, 1 / 4.); if (T_J > MAXTEMP) { printout("[warning] temperature estimator T_J = %g exceeds T_max %g in cell %d. Setting T_J = T_max!\n", T_J, MAXTEMP, modelgridindex); @@ -1050,12 +1018,12 @@ static void set_params_fullspec(const int modelgridindex, const int timestep) { } grid::set_TR(modelgridindex, T_R); - const float W = J[modelgridindex] * PI / STEBO / pow(T_R, 4); + const float W = J[nonemptymgi] * PI / STEBO / pow(T_R, 4); grid::set_W(modelgridindex, W); printout( "Full-spectrum fit radfield for cell %d at timestep %d: J %g, nubar %5.1f Angstrom, T_J %g, T_R %g, W %g\n", - modelgridindex, timestep, J[modelgridindex], 1e8 * CLIGHT / nubar, T_J, T_R, W); + modelgridindex, timestep, J[nonemptymgi], 1e8 * CLIGHT / nubar, T_J, T_R, W); } } @@ -1065,9 +1033,11 @@ void fit_parameters(int modelgridindex, int timestep) { set_params_fullspec(modelgridindex, timestep); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - if (J_normfactor[modelgridindex] <= 0) { - printout("radfield: FATAL J_normfactor = %g in cell %d at call to fit_parameters", J_normfactor[modelgridindex], + if (J_normfactor[nonemptymgi] <= 0) { + printout("radfield: FATAL J_normfactor = %g in cell %d at call to fit_parameters", J_normfactor[nonemptymgi], modelgridindex); abort(); } @@ -1077,15 +1047,13 @@ void fit_parameters(int modelgridindex, int timestep) J_bin_sum += get_bin_J(modelgridindex, binindex); } - printout("radfield bins sum to J of %g (%.1f%% of total J).\n", J_bin_sum, 100. * J_bin_sum / J[modelgridindex]); + printout("radfield bins sum to J of %g (%.1f%% of total J).\n", J_bin_sum, 100. * J_bin_sum / J[nonemptymgi]); printout("radfield: Finding parameters for %d bins...\n", RADFIELDBINCOUNT); double J_bin_max = 0.; for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { const double J_bin = get_bin_J(modelgridindex, binindex); - if (J_bin > J_bin_max) { - J_bin_max = J_bin; - } + J_bin_max = std::max(J_bin_max, J_bin); } for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { @@ -1151,7 +1119,7 @@ void fit_parameters(int modelgridindex, int timestep) // T_R_bin = -1; // W_bin = -1; // } - const int mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; radfieldbin_solutions[mgibinindex].T_R = T_R_bin; radfieldbin_solutions[mgibinindex].W = W_bin; } @@ -1178,11 +1146,15 @@ void fit_parameters(int modelgridindex, int timestep) } } -void set_J_normfactor(int modelgridindex, double normfactor) { J_normfactor[modelgridindex] = normfactor; } +void set_J_normfactor(int modelgridindex, double normfactor) { + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + J_normfactor[nonemptymgi] = normfactor; +} void normalise_J(const int modelgridindex, const double estimator_normfactor_over4pi) { - assert_always(std::isfinite(J[modelgridindex])); - J[modelgridindex] *= estimator_normfactor_over4pi; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_always(std::isfinite(J[nonemptymgi])); + J[nonemptymgi] *= estimator_normfactor_over4pi; for (int i = 0; i < detailed_linecount; i++) { prev_Jb_lu_normed[modelgridindex][i].value = Jb_lu_raw[modelgridindex][i].value * estimator_normfactor_over4pi; prev_Jb_lu_normed[modelgridindex][i].contribcount = Jb_lu_raw[modelgridindex][i].contribcount; @@ -1204,13 +1176,16 @@ void normalise_bf_estimators(const int modelgridindex, const double estimator_no static auto get_bfcontindex(const int element, const int lowerion, const int lower, const int phixstargetindex) -> int { // simple linear search seems to be faster than the binary search // possibly because lower frequency transitions near start of list are more likely to be called? - for (int i = 0; i < globals::nbfcontinua; i++) { - if ((globals::allcont[i].element == element) && (globals::allcont[i].ion == lowerion) && - (globals::allcont[i].level == lower) && (globals::allcont[i].phixstargetindex == phixstargetindex)) { - return i; - } - } + const auto &matchbf = std::find_if(globals::allcont, globals::allcont + globals::nbfcontinua, [=](const auto &bf) { + return (bf.element == element) && (bf.ion == lowerion) && (bf.level == lower) && + (bf.phixstargetindex == phixstargetindex); + }); + + const int bfcontindex = std::distance(globals::allcont, matchbf); + if (bfcontindex < globals::nbfcontinua) { + return bfcontindex; + } // not found in the continua list return -1; } @@ -1233,12 +1208,14 @@ auto get_bfrate_estimator(const int element, const int lowerion, const int lower } void normalise_nuJ(const int modelgridindex, const double estimator_normfactor_over4pi) { - assert_always(std::isfinite(nuJ[modelgridindex])); - nuJ[modelgridindex] *= estimator_normfactor_over4pi; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_always(std::isfinite(nuJ[nonemptymgi])); + nuJ[nonemptymgi] *= estimator_normfactor_over4pi; } auto get_T_J_from_J(const int modelgridindex) -> double { - const double T_J = pow(J[modelgridindex] * PI / STEBO, 1. / 4.); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const double T_J = pow(J[nonemptymgi] * PI / STEBO, 1. / 4.); if (!std::isfinite(T_J)) { /// keep old value of T_J printout("[warning] get_T_J_from_J: T_J estimator infinite in cell %d, use value of last timestep\n", @@ -1259,17 +1236,19 @@ auto get_T_J_from_J(const int modelgridindex) -> double { #ifdef DO_TITER void titer_J(const int modelgridindex) { - if (J_reduced_save[modelgridindex] >= 0) { - J[modelgridindex] = (J[modelgridindex] + J_reduced_save[modelgridindex]) / 2; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + if (J_reduced_save[nonemptymgi] >= 0) { + J[nonemptymgi] = (J[nonemptymgi] + J_reduced_save[nonemptymgi]) / 2; } - J_reduced_save[modelgridindex] = J[modelgridindex]; + J_reduced_save[nonemptymgi] = J[nonemptymgi]; } void titer_nuJ(const int modelgridindex) { - if (nuJ_reduced_save[modelgridindex] >= 0) { - nuJ[modelgridindex] = (nuJ[modelgridindex] + nuJ_reduced_save[modelgridindex]) / 2; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + if (nuJ_reduced_save[nonemptymgi] >= 0) { + nuJ[nonemptymgi] = (nuJ[nonemptymgi] + nuJ_reduced_save[nonemptymgi]) / 2; } - nuJ_reduced_save[modelgridindex] = nuJ[modelgridindex]; + nuJ_reduced_save[nonemptymgi] = nuJ[nonemptymgi]; } #endif @@ -1277,11 +1256,13 @@ void titer_nuJ(const int modelgridindex) { void reduce_estimators() // reduce and broadcast (allreduce) the estimators for J and nuJ in all bins { - MPI_Allreduce(MPI_IN_PLACE, J, grid::get_npts_model(), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, nuJ, grid::get_npts_model(), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + const int nonempty_npts_model = grid::get_nonempty_npts_model(); + + MPI_Allreduce(MPI_IN_PLACE, J.data(), nonempty_npts_model, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, nuJ.data(), nonempty_npts_model, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); if constexpr (DETAILED_BF_ESTIMATORS_ON) { - MPI_Allreduce(MPI_IN_PLACE, bfrate_raw, grid::get_nonempty_npts_model() * globals::nbfcontinua, MPI_DOUBLE, MPI_SUM, + MPI_Allreduce(MPI_IN_PLACE, bfrate_raw, nonempty_npts_model * globals::nbfcontinua, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); } @@ -1290,22 +1271,12 @@ void reduce_estimators() printout("Reducing binned radiation field estimators"); assert_always(radfieldbins != nullptr); - for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { - // printout("DEBUGCELLS: cell %d associated_cells %d\n", modelgridindex, - // grid::get_numassociatedcells(modelgridindex)); - if (grid::get_numassociatedcells(modelgridindex) > 0) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; - // printout("MPI: pre-MPI_Allreduce, this process modelgrid %d binindex %d has a individual contribcount of - // %d\n",modelgridindex,binindex,radfieldbins[mgibinindex].contribcount); - MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].J_raw, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].nuJ_raw, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].contribcount, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); - - // printout("MPI: After MPI_Allreduce: this process modelgrid %d binindex %d has a contribcount of - // %d\n",modelgridindex,binindex,radfieldbins[mgibinindex].contribcount); - } + for (int nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { + for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].J_raw, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].nuJ_raw, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &radfieldbins[mgibinindex].contribcount, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); } } const int duration_reduction = time(nullptr) - sys_time_start_reduction; @@ -1317,8 +1288,6 @@ void reduce_estimators() printout("Reducing detailed line estimators"); for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { - // printout("DEBUGCELLS: cell %d associated_cells %d\n", modelgridindex, - // grid::get_numassociatedcells(modelgridindex)); if (grid::get_numassociatedcells(modelgridindex) > 0) { for (int jblueindex = 0; jblueindex < detailed_linecount; jblueindex++) { MPI_Allreduce(MPI_IN_PLACE, &Jb_lu_raw[modelgridindex][jblueindex].value, 1, MPI_DOUBLE, MPI_SUM, @@ -1338,36 +1307,40 @@ void do_MPI_Bcast(const int modelgridindex, const int root, int root_node_id) // broadcast computed radfield results including parameters // from the cells belonging to root process to all processes { - MPI_Bcast(&J_normfactor[modelgridindex], 1, MPI_DOUBLE, root, MPI_COMM_WORLD); - if (grid::get_numassociatedcells(modelgridindex) > 0) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; - if (globals::rank_in_node == 0) { - MPI_Bcast(&radfieldbin_solutions[mgibinindex].W, 1, MPI_FLOAT, root_node_id, globals::mpi_comm_internode); - MPI_Bcast(&radfieldbin_solutions[mgibinindex].T_R, 1, MPI_FLOAT, root_node_id, globals::mpi_comm_internode); - } - MPI_Bcast(&radfieldbins[mgibinindex].J_raw, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); - MPI_Bcast(&radfieldbins[mgibinindex].nuJ_raw, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); - MPI_Bcast(&radfieldbins[mgibinindex].contribcount, 1, MPI_INT, root, MPI_COMM_WORLD); - } - } + if (grid::get_numassociatedcells(modelgridindex) == 0) { + return; + } + + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + MPI_Bcast(&J_normfactor[nonemptymgi], 1, MPI_DOUBLE, root, MPI_COMM_WORLD); - if constexpr (DETAILED_BF_ESTIMATORS_ON) { + if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { + for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { + const int mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; if (globals::rank_in_node == 0) { - MPI_Bcast(&prev_bfrate_normed[nonemptymgi * globals::nbfcontinua], globals::nbfcontinua, MPI_FLOAT, - root_node_id, globals::mpi_comm_internode); + MPI_Bcast(&radfieldbin_solutions[mgibinindex].W, 1, MPI_FLOAT, root_node_id, globals::mpi_comm_internode); + MPI_Bcast(&radfieldbin_solutions[mgibinindex].T_R, 1, MPI_FLOAT, root_node_id, globals::mpi_comm_internode); } + MPI_Bcast(&radfieldbins[mgibinindex].J_raw, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); + MPI_Bcast(&radfieldbins[mgibinindex].nuJ_raw, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); + MPI_Bcast(&radfieldbins[mgibinindex].contribcount, 1, MPI_INT, root, MPI_COMM_WORLD); } + } - if (DETAILED_LINE_ESTIMATORS_ON) { - for (int jblueindex = 0; jblueindex < detailed_linecount; jblueindex++) { - MPI_Bcast(&prev_Jb_lu_normed[modelgridindex][jblueindex].value, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); - MPI_Bcast(&prev_Jb_lu_normed[modelgridindex][jblueindex].contribcount, 1, MPI_INT, root, MPI_COMM_WORLD); - } + if constexpr (DETAILED_BF_ESTIMATORS_ON) { + if (globals::rank_in_node == 0) { + MPI_Bcast(&prev_bfrate_normed[nonemptymgi * globals::nbfcontinua], globals::nbfcontinua, MPI_FLOAT, root_node_id, + globals::mpi_comm_internode); + } + } + + if constexpr (DETAILED_LINE_ESTIMATORS_ON) { + for (int jblueindex = 0; jblueindex < detailed_linecount; jblueindex++) { + MPI_Bcast(&prev_Jb_lu_normed[modelgridindex][jblueindex].value, 1, MPI_DOUBLE, root, MPI_COMM_WORLD); + MPI_Bcast(&prev_Jb_lu_normed[modelgridindex][jblueindex].contribcount, 1, MPI_INT, root, MPI_COMM_WORLD); } } + MPI_Barrier(MPI_COMM_WORLD); } #endif @@ -1413,7 +1386,7 @@ void write_restart_data(FILE *gridsave_file) { if (grid::get_numassociatedcells(modelgridindex) > 0) { const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); assert_testmodeonly(nonemptymgi >= 0); - fprintf(gridsave_file, "%d %la\n", modelgridindex, J_normfactor[modelgridindex]); + fprintf(gridsave_file, "%d %la\n", modelgridindex, J_normfactor[nonemptymgi]); if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { @@ -1510,7 +1483,7 @@ void read_restart_data(FILE *gridsave_file) { } } - if (DETAILED_LINE_ESTIMATORS_ON) { + if constexpr (DETAILED_LINE_ESTIMATORS_ON) { int detailed_linecount_in = 0; assert_always(fscanf(gridsave_file, "%d\n", &detailed_linecount_in) == 1); @@ -1529,7 +1502,7 @@ void read_restart_data(FILE *gridsave_file) { if (grid::get_numassociatedcells(modelgridindex) > 0) { const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); int mgi_in = 0; - assert_always(fscanf(gridsave_file, "%d %la\n", &mgi_in, &J_normfactor[modelgridindex]) == 2); + assert_always(fscanf(gridsave_file, "%d %la\n", &mgi_in, &J_normfactor[nonemptymgi]) == 2); if (mgi_in != modelgridindex) { printout("ERROR: expected data for cell %d but found cell %d\n", modelgridindex, mgi_in); abort(); @@ -1572,10 +1545,10 @@ void read_restart_data(FILE *gridsave_file) { // across the binned radiation field which is discontinuous at the bin boundaries inline auto integrate(const gsl_function *f, double nu_a, double nu_b, double epsabs, double epsrel, size_t limit, int key, gsl_integration_workspace *workspace, double *result, double *abserr) -> int { - if (MULTIBIN_RADFIELD_MODEL_ON && (globals::nts_global >= FIRST_NLTE_RADFIELD_TIMESTEP)) { + if (MULTIBIN_RADFIELD_MODEL_ON && (globals::timestep >= FIRST_NLTE_RADFIELD_TIMESTEP)) { auto *pts = static_cast(malloc((RADFIELDBINCOUNT + 3) * sizeof(double))); int binindex_a = select_bin(nu_a); - int const binindex_b = select_bin(nu_b); + const int binindex_b = select_bin(nu_b); int npts = 0; pts[npts++] = nu_a; if (binindex_a == binindex_b) // both higher, both lower, or match the same bin diff --git a/radfield.h b/radfield.h index 59cea7938..690f842c6 100644 --- a/radfield.h +++ b/radfield.h @@ -8,6 +8,7 @@ #include "sn3d.h" namespace radfield { + void zero_estimators(int modelgridindex); void init(int my_rank, int ndo_nonempty); void initialise_prev_titer_photoionestimators(); @@ -16,7 +17,6 @@ void close_file(); void update_estimators(int modelgridindex, double distance_e_cmf, double nu_cmf, const struct packet *pkt_ptr); void update_lineestimator(int modelgridindex, int lineindex, double increment); double radfield(double nu, int modelgridindex); -double dbb_mgi(double nu, int modelgridindex); void fit_parameters(int modelgridindex, int timestep); void set_J_normfactor(int modelgridindex, double normfactor); void normalise_J(int modelgridindex, double estimator_normfactor_over4pi); @@ -39,9 +39,8 @@ void reset_bfrate_contributions(int modelgridindex); int integrate(const gsl_function *f, double nu_a, double nu_b, double epsabs, double epsrel, size_t limit, int key, gsl_integration_workspace *workspace, double *result, double *abserr); -template -constexpr double dbb(double nu, T_t T, W_t W) -// returns J_nu for a dilute black body [ergs/s/sr/cm2/Hz] +constexpr double dbb(double nu, auto T, auto W) +// returns J_nu [ergs/s/sr/cm2/Hz] for a dilute black body with temperature T and dilution factor W { return W * TWOHOVERCLIGHTSQUARED * std::pow(nu, 3) / std::expm1(HOVERKB * nu / T); } diff --git a/ratecoeff.cc b/ratecoeff.cc index e054452e7..a81ec589d 100644 --- a/ratecoeff.cc +++ b/ratecoeff.cc @@ -5,8 +5,9 @@ #include #include #include -// #define _XOPEN_SOURCE #define D_POSIX_SOURCE +#include + #include #include "artisoptions.h" @@ -31,9 +32,15 @@ // int cellnumber; // } gslintegration_bfheatingparas; -static double T_step; double T_step_log; +static double *spontrecombcoeffs = nullptr; + +// for USE_LUT_PHOTOION = true +static double *corrphotoioncoeffs = nullptr; + +static double *bfcooling_coeffs = nullptr; + using gsl_integral_paras_gammacorr = struct { double nu_edge; double departure_ratio; @@ -46,6 +53,65 @@ static char adatafile_hash[33]; static char compositionfile_hash[33]; std::array phixsfile_hash; +void setup_photoion_luts() { + size_t mem_usage_photoionluts = 2 * TABLESIZE * globals::nbfcontinua * sizeof(double); + + if (globals::nbfcontinua > 0) { +#ifdef MPI_ON + MPI_Win win = MPI_WIN_NULL; + MPI_Aint size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + int disp_unit = sizeof(double); + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &spontrecombcoeffs, + &win) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &spontrecombcoeffs) == MPI_SUCCESS); +#else + spontrecombcoeffs = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); +#endif + assert_always(spontrecombcoeffs != nullptr); + + if constexpr (USE_LUT_PHOTOION) { +#ifdef MPI_ON + size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &corrphotoioncoeffs, + &win) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &corrphotoioncoeffs) == MPI_SUCCESS); +#else + corrphotoioncoeffs = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); +#endif + assert_always(corrphotoioncoeffs != nullptr); + mem_usage_photoionluts += TABLESIZE * globals::nbfcontinua * sizeof(double); + } + + if constexpr (USE_LUT_BFHEATING) { +#ifdef MPI_ON + size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, + &globals::bfheating_coeff, &win) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &globals::bfheating_coeff) == MPI_SUCCESS); +#else + globals::bfheating_coeff = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); +#endif + assert_always(globals::bfheating_coeff != nullptr); + mem_usage_photoionluts += TABLESIZE * globals::nbfcontinua * sizeof(double); + } + +#ifdef MPI_ON + size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &bfcooling_coeffs, + &win) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win, 0, &size, &disp_unit, &bfcooling_coeffs) == MPI_SUCCESS); +#else + bfcooling_coeffs = static_cast(malloc(TABLESIZE * globals::nbfcontinua * sizeof(double))); +#endif + assert_always(bfcooling_coeffs != nullptr); + } + + printout( + "[info] mem_usage: lookup tables derived from photoionisation (spontrecombcoeff, bfcooling and " + "corrphotoioncoeff/bfheating if enabled) occupy %.3f MB\n", + mem_usage_photoionluts / 1024. / 1024.); +} + static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool /// Try to read in the precalculated rate coefficients from file /// return true if successful or false otherwise @@ -145,7 +211,7 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool assert_always( fscanf(ratecoeff_file, "%d %d %d %d\n", &in_element, &in_ionstage, &in_levels, &in_ionisinglevels) == 4); const int nlevels = get_nlevels(element, ion); - int const ionisinglevels = get_ionisinglevels(element, ion); + const int ionisinglevels = get_ionisinglevels(element, ion); if (get_atomicnumber(element) != in_element || get_ionstage(element, ion) != in_ionstage || nlevels != in_levels || ionisinglevels != in_ionisinglevels) { printout( @@ -160,7 +226,7 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool printout("Existing ratecoeff.dat is valid. Reading this file...\n"); for (int element = 0; element < get_nelements(); element++) { - int const nions = get_nions(element) - 1; + const int nions = get_nions(element) - 1; for (int ion = 0; ion < nions; ion++) { // nlevels = get_nlevels(element,ion); const int nlevels = get_ionisinglevels(element, ion); /// number of ionising levels associated with current ion @@ -172,23 +238,22 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { /// Loop over the temperature grid for (int iter = 0; iter < TABLESIZE; iter++) { - double alpha_sp = NAN; - double bfcooling_coeff = NAN; - double corrphotoioncoeff = NAN; - double bfheating_coeff = NAN; - assert_always(fscanf(ratecoeff_file, "%la %la %la %la\n", &alpha_sp, &bfcooling_coeff, &corrphotoioncoeff, - &bfheating_coeff) == 4); + double in_alpha_sp = NAN; + double in_bfcooling_coeff = NAN; + double in_corrphotoioncoeff = NAN; + double in_bfheating_coeff = NAN; + assert_always(fscanf(ratecoeff_file, "%la %la %la %la\n", &in_alpha_sp, &in_bfcooling_coeff, + &in_corrphotoioncoeff, &in_bfheating_coeff) == 4); // assert_always(std::isfinite(alpha_sp) && alpha_sp >= 0); - globals::spontrecombcoeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] = alpha_sp; + spontrecombcoeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] = in_alpha_sp; // assert_always(std::isfinite(bfcooling_coeff) && bfcooling_coeff >= 0); - globals::bfcooling_coeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] = bfcooling_coeff; + bfcooling_coeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] = in_bfcooling_coeff; if constexpr (USE_LUT_PHOTOION) { - if (corrphotoioncoeff >= 0) { - globals::corrphotoioncoeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] = - corrphotoioncoeff; + if (in_corrphotoioncoeff >= 0) { + corrphotoioncoeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] = in_corrphotoioncoeff; } else { printout( "ERROR: USE_LUT_PHOTOION is on, but there are no corrphotoioncoeff values in ratecoeff file\n"); @@ -196,8 +261,9 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool } } if constexpr (USE_LUT_BFHEATING) { - if (bfheating_coeff >= 0) { - globals::bfheating_coeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] = bfheating_coeff; + if (in_bfheating_coeff >= 0) { + globals::bfheating_coeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] = + in_bfheating_coeff; } else { printout( "ERROR: USE_LUT_BFHEATING is on, but there are no bfheating_coeff values in the ratecoeff " @@ -243,13 +309,10 @@ static void write_ratecoeff_dat() { /// Loop over the temperature grid for (int iter = 0; iter < TABLESIZE; iter++) { const int bflutindex = get_bflutindex(iter, element, ion, level, phixstargetindex); - const double alpha_sp = globals::spontrecombcoeff[bflutindex]; - const double bfcooling_coeff = globals::bfcooling_coeff[bflutindex]; - const double corrphotoioncoeff = !USE_LUT_PHOTOION ? -1 : globals::corrphotoioncoeff[bflutindex]; - const double bfheating_coeff = !USE_LUT_BFHEATING ? -1 : globals::bfheating_coeff[bflutindex]; - - fprintf(ratecoeff_file, "%la %la %la %la\n", alpha_sp, bfcooling_coeff, corrphotoioncoeff, bfheating_coeff); + fprintf(ratecoeff_file, "%la %la %la %la\n", spontrecombcoeffs[bflutindex], bfcooling_coeffs[bflutindex], + !USE_LUT_PHOTOION ? -1 : corrphotoioncoeffs[bflutindex], + !USE_LUT_BFHEATING ? -1 : globals::bfheating_coeff[bflutindex]); } } } @@ -258,9 +321,6 @@ static void write_ratecoeff_dat() { fclose(ratecoeff_file); } -///**************************************************************************** -/// The following functions define the integrands for these rate coefficients -/// for use with libgsl integrators. static auto alpha_sp_integrand_gsl(const double nu, void *const voidparas) -> double /// Integrand to calculate the rate coefficient for spontaneous recombination /// using gsl integrators. @@ -294,34 +354,10 @@ static auto alpha_sp_E_integrand_gsl(const double nu, void *const voidparas) -> return x; } -/*static double gamma_integrand_gsl(double nu, void *paras) -/// Integrand to calculate the rate coefficient for photoionization -/// using gsl integrators. -{ - double T = ((gslintegration_paras *) paras)->T; - double nu_edge = ((gslintegration_paras *) paras)->nu_edge; - - /// Information about the current level is passed via the global variable - /// mastate[tid] and its child values element, ion, level - /// MAKE SURE THAT THESE ARE SET IN THE CALLING FUNCTION!!!!!!!!!!!!!!!!! - double sigma_bf = photoionization_crosssection_fromtable(params->photoion_xs, nu_edge,nu); - - /// Dependence on dilution factor W is linear. This allows to set it here to - /// 1. and scale to its actual value later on. - double x = sigma_bf / H / nu * radfield::dbb(nu,T,1.); - //x = sigma_bf/H/nu * radfield::dbb(nu,T,1.); - //if (HOVERKB*nu/T < 1e-2) x = sigma_bf * pow(nu,2)/(HOVERKB*nu/T); - //else if (HOVERKB*nu/T >= 1e2) x = sigma_bf * pow(nu,2)*exp(-HOVERKB*nu/T); - //else x = sigma_bf * pow(nu,2)/(exp(HOVERKB*nu/T)-1); - - return x; -}*/ - static auto gammacorr_integrand_gsl(const double nu, void *const voidparas) -> double /// Integrand to calculate the rate coefficient for photoionization /// using gsl integrators. Corrected for stimulated recombination. { - assert_testmodeonly(USE_LUT_PHOTOION); const gslintegration_paras *const params = static_cast(voidparas); const float T = params->T; @@ -341,8 +377,6 @@ static auto approx_bfheating_integrand_gsl(const double nu, void *const voidpara /// formula. The radiation fields dependence on W is taken into account by multiplying /// the resulting expression with the correct W later on. { - assert_testmodeonly(USE_LUT_BFHEATING); - const gslintegration_paras *const params = static_cast(voidparas); const float T = params->T; @@ -366,30 +400,6 @@ static auto approx_bfheating_integrand_gsl(const double nu, void *const voidpara return x; } -/*double bfheating_integrand_gsl(double nu, void *paras) -/// Integrand to calculate the modified rate coefficient for photoionization -/// using gsl integrators. -{ - double get_groundlevelpop(int cellnumber, int element, int ion); - double x; - - int cellnumber = ((gslintegration_bfheatingparas *) paras)->cellnumber; - double nu_edge = ((gslintegration_bfheatingparas *) paras)->nu_edge; - - float T_e = globals::cell[cellnumber].T_e; - double T_R = globals::cell[cellnumber].T_R; - double W = globals::cell[cellnumber].W; - float nne = globals::cell[cellnumber].nne; - - double sigma_bf = photoionization_crosssection_fromtable(params->photoion_xs, nu_edge,nu); - double E_threshold = nu_edge*H; - double sfac = calculate_sahafact(element,ion,level,upperionlevel,T_e,E_threshold); - double nnionlevel = get_groundlevelpop(cellnumber,element,ion+1); - - x = sigma_bf*(1-nu_edge/nu)*radfield::dbb(nu,T_R,W) * (1-nnionlevel*nne/nnlevel*sf*exp(-H*nu/KB/T_e)); - return x; -}*/ - static auto bfcooling_integrand_gsl(const double nu, void *const voidparas) -> double /// Integrand to precalculate the bound-free heating ratecoefficient in an approximative way /// on a temperature grid using the assumption that T_e=T_R and W=1 in the ionisation @@ -511,7 +521,7 @@ static void precalculate_rate_coefficient_integrals() { double error = NAN; int status = 0; const float T_e = MINTEMP * exp(iter * T_step_log); - // T_e = MINTEMP + iter*T_step; + const double sfac = calculate_sahafact(element, ion, level, upperlevel, T_e, E_threshold); // printout("%d %g\n",iter,T_e); @@ -553,30 +563,7 @@ static void precalculate_rate_coefficient_integrals() { alpha_sp = 0; } // assert_always(alpha_sp >= 0); - globals::spontrecombcoeff[bflutindex] = alpha_sp; - - // if (atomic_number == 26 && ionstage == 3 && level < 5) - // { - // const double E_threshold_b = get_phixs_threshold(element, ion, level, phixstargetindex); - // const double sfac_b = calculate_sahafact(element,ion,level,upperlevel,T_e,E_threshold_b); - // const double nu_threshold_b = E_threshold_b / H; - // const double nu_max_phixs_b = nu_threshold * last_phixs_nuovernuedge; //nu of the uppermost point in - // the phixs table intparas.nu_edge = nu_threshold_b; // Global variable which passes the - // threshold to the integrator - // // the threshold of the first target gives nu of the - // first phixstable point - // double alpha_sp_new; - // status = gsl_integration_qag(&F_alpha_sp, nu_threshold_b, nu_max_phixs_b, 0, intaccuracy, GSLWSIZE, - // GSL_INTEG_GAUSS61, w, &alpha_sp_new, &error); alpha_sp_new *= FOURPI * sfac_b * phixstargetprobability; - // printout("recomb: T_e %6.1f Z=%d ionstage %d->%d upper+1 %5d lower+1 %5d sfac %7.2e sfac_new %7.2e - // alpha %7.2e alpha_new %7.2e threshold_ev %7.2e threshold_new_ev %7.2e\n", - // T_e, get_atomicnumber(element), get_ionstage(element, ion + 1), - // get_ionstage(element, ion), upperlevel + 1, level + 1, - // sfac, sfac_b, - // alpha_sp, alpha_sp_new, - // E_threshold / EV, - // E_threshold_b / EV); - // } + spontrecombcoeffs[bflutindex] = alpha_sp; // if (iter == 0) // printout("alpha_sp: element %d ion %d level %d upper level %d at temperature %g, alpha_sp is %g @@ -598,46 +585,46 @@ static void precalculate_rate_coefficient_integrals() { printout("WARNING: gammacorr was negative for level %d\n", level); gammacorr = 0; } - globals::corrphotoioncoeff[bflutindex] = gammacorr; + corrphotoioncoeffs[bflutindex] = gammacorr; } if constexpr (USE_LUT_BFHEATING) { - double bfheating_coeff = 0.0; + double this_bfheating_coeff = 0.0; const gsl_function F_bfheating = {.function = &approx_bfheating_integrand_gsl, .params = &intparas}; status = gsl_integration_qag(&F_bfheating, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &bfheating_coeff, &error); + GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &this_bfheating_coeff, &error); - if (status != 0 && (status != 18 || (error / bfheating_coeff) > epsrelwarning)) { + if (status != 0 && (status != 18 || (error / this_bfheating_coeff) > epsrelwarning)) { printout("bfheating_coeff integrator status %d. Integral value %9.3e +/- %9.3e\n", status, - bfheating_coeff, error); + this_bfheating_coeff, error); } - bfheating_coeff *= FOURPI * phixstargetprobability; - if (bfheating_coeff < 0) { + this_bfheating_coeff *= FOURPI * phixstargetprobability; + if (this_bfheating_coeff < 0) { printout("WARNING: bfheating_coeff was negative for level %d\n", level); - bfheating_coeff = 0; + this_bfheating_coeff = 0; } - globals::bfheating_coeff[bflutindex] = bfheating_coeff; + globals::bfheating_coeff[bflutindex] = this_bfheating_coeff; } - double bfcooling_coeff = 0.0; + double this_bfcooling_coeff = 0.0; const gsl_function F_bfcooling = {.function = &bfcooling_integrand_gsl, .params = &intparas}; status = gsl_integration_qag(&F_bfcooling, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &bfcooling_coeff, &error); - if (status != 0 && (status != 18 || (error / bfcooling_coeff) > epsrelwarning)) { + GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &this_bfcooling_coeff, &error); + if (status != 0 && (status != 18 || (error / this_bfcooling_coeff) > epsrelwarning)) { printout("bfcooling_coeff integrator status %d. Integral value %9.3e +/- %9.3e\n", status, - bfcooling_coeff, error); + this_bfcooling_coeff, error); } - bfcooling_coeff *= FOURPI * sfac * phixstargetprobability; - if (!std::isfinite(bfcooling_coeff) || bfcooling_coeff < 0) { + this_bfcooling_coeff *= FOURPI * sfac * phixstargetprobability; + if (!std::isfinite(this_bfcooling_coeff) || this_bfcooling_coeff < 0) { printout( "WARNING: bfcooling_coeff was negative or non-finite for level %d Te %g. bfcooling_coeff %g sfac %g " "phixstargetindex %d phixstargetprobability %g\n", - level, T_e, bfcooling_coeff, sfac, phixstargetindex, phixstargetprobability); - bfcooling_coeff = 0; + level, T_e, this_bfcooling_coeff, sfac, phixstargetindex, phixstargetprobability); + this_bfcooling_coeff = 0; } - globals::bfcooling_coeff[bflutindex] = bfcooling_coeff; + bfcooling_coeffs[bflutindex] = this_bfcooling_coeff; } } } @@ -663,11 +650,6 @@ auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel const double zrand = 1. - rng_uniform(); // Make sure that 0 < zrand <= 1 - // printout("emitted bf photon Z=%2d ionstage %d->%d upper %4d lower %4d lambda %7.1f lambda_edge %7.1f ratio %g zrand - // %g\n", - // get_atomicnumber(element), get_ionstage(element, lowerion + 1), get_ionstage(element, lowerion), upperionlevel, - // lower, 1e8 * CLIGHT / nu_selected, 1e8 * CLIGHT / nu_threshold, nu_selected / nu_threshold, zrand); - const double deltanu = (nu_max_phixs - nu_threshold) / npieces; double error = NAN; @@ -707,10 +689,6 @@ auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel auto get_spontrecombcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double /// Returns the rate coefficient for spontaneous recombination. { - /*int lowerindex = floor((T-MINTEMP)/T_step); - int upperindex = lowerindex + 1; - double T_upper = MINTEMP + upperindex*T_step; - double T_lower = MINTEMP + lowerindex*T_step;*/ double Alpha_sp = NAN; const int lowerindex = floor(log(T_e / MINTEMP) / T_step_log); assert_always(lowerindex >= 0); @@ -719,13 +697,13 @@ auto get_spontrecombcoeff(int element, int ion, int level, int phixstargetindex, const double T_lower = MINTEMP * exp(lowerindex * T_step_log); const double T_upper = MINTEMP * exp(upperindex * T_step_log); - const double f_upper = globals::spontrecombcoeff[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; - const double f_lower = globals::spontrecombcoeff[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; + const double f_upper = spontrecombcoeffs[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; + const double f_lower = spontrecombcoeffs[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; // printout("interpolate_spontrecombcoeff element %d, ion %d, level %d, upper %g, lower %g\n", // element,ion,level,f_upper,f_lower); Alpha_sp = (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T_e - T_lower)); } else { - Alpha_sp = globals::spontrecombcoeff[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; + Alpha_sp = spontrecombcoeffs[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; } return Alpha_sp; } @@ -844,17 +822,17 @@ static void scale_level_phixs(const int element, const int ion, const int level, const int nphixstargets = get_nphixstargets(element, ion, level); for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { for (int iter = 0; iter < TABLESIZE; iter++) { - globals::spontrecombcoeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; + spontrecombcoeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; if constexpr (USE_LUT_PHOTOION) { - globals::corrphotoioncoeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; + corrphotoioncoeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; } if constexpr (USE_LUT_BFHEATING) { globals::bfheating_coeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; } - globals::bfcooling_coeff[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; + bfcooling_coeffs[get_bflutindex(iter, element, ion, level, phixstargetindex)] *= factor; } } } @@ -1030,7 +1008,6 @@ void ratecoefficients_init() /// T_e = T_R for this precalculation. { /// Determine the temperture grids gridsize - T_step = (1. * MAXTEMP - MINTEMP) / (TABLESIZE - 1.); /// global variables T_step_log = (log(MAXTEMP) - log(MINTEMP)) / (TABLESIZE - 1.); md5_file("adata.txt", adatafile_hash); @@ -1087,14 +1064,12 @@ auto interpolate_corrphotoioncoeff(int element, int ion, int level, int phixstar const double T_lower = MINTEMP * exp(lowerindex * T_step_log); const double T_upper = MINTEMP * exp(upperindex * T_step_log); - const double f_upper = - globals::corrphotoioncoeff[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; - const double f_lower = - globals::corrphotoioncoeff[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; + const double f_upper = corrphotoioncoeffs[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; + const double f_lower = corrphotoioncoeffs[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; return (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T - T_lower)); } - return globals::corrphotoioncoeff[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; + return corrphotoioncoeffs[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; } auto get_corrphotoioncoeff_ana(int element, int ion, int level, int phixstargetindex, int modelgridindex) -> double @@ -1210,6 +1185,21 @@ auto get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetin return stimrecombcoeff; } +auto get_bfcoolingcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double { + const int lowerindex = floor(log(T_e / MINTEMP) / T_step_log); + if (lowerindex < TABLESIZE - 1) { + const int upperindex = lowerindex + 1; + const double T_lower = MINTEMP * exp(lowerindex * T_step_log); + const double T_upper = MINTEMP * exp(upperindex * T_step_log); + + const double f_upper = bfcooling_coeffs[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; + const double f_lower = bfcooling_coeffs[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; + + return (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T_e - T_lower)); + } + return bfcooling_coeffs[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; +} + static auto integrand_corrphotoioncoeff_custom_radfield(const double nu, void *const voidparas) -> double /// Integrand to calculate the rate coefficient for photoionization /// using gsl integrators. Corrected for stimulated recombination. @@ -1229,7 +1219,6 @@ static auto integrand_corrphotoioncoeff_custom_radfield(const double nu, void *c const float sigma_bf = photoionization_crosssection_fromtable(params->photoion_xs, params->nu_edge, nu); - // const double Jnu = use_cellhist ? radfield::radfield(nu, modelgridindex) : radfield::dbb_mgi(nu, modelgridindex); const double Jnu = radfield::radfield(nu, modelgridindex); // TODO: MK thesis page 41, use population ratios and Te? @@ -1309,7 +1298,7 @@ auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex /// correction may be evaluated at T_R! double gammacorr = -1; - if (DETAILED_BF_ESTIMATORS_ON && globals::nts_global >= DETAILED_BF_ESTIMATORS_USEFROMTIMESTEP) { + if (DETAILED_BF_ESTIMATORS_ON && globals::timestep >= DETAILED_BF_ESTIMATORS_USEFROMTIMESTEP) { gammacorr = radfield::get_bfrate_estimator(element, ion, level, phixstargetindex, modelgridindex); // gammacorr will be -1 if no estimators available if (gammacorr > 0) { @@ -1338,8 +1327,8 @@ auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex const int index_in_groundlevelcontestimator = globals::elements[element].ions[ion].levels[level].closestgroundlevelcont; if (index_in_groundlevelcontestimator >= 0) { - gammacorr *= globals::corrphotoionrenorm[modelgridindex * get_nelements() * get_max_nions() + - index_in_groundlevelcontestimator]; + gammacorr *= + globals::corrphotoionrenorm[get_ionestimindex(modelgridindex, 0, 0) + index_in_groundlevelcontestimator]; } } } @@ -1365,7 +1354,7 @@ static auto get_nlevels_important(int modelgridindex, int element, int ion, bool return get_nlevels(element, ion); } // get the stored ion population for comparison with the cumulative sum of level pops - const double nnion_real = ionstagepop(modelgridindex, element, ion); + const double nnion_real = get_nnion(modelgridindex, element, ion); double nnlevelsum = 0.; int nlevels_important = get_ionisinglevels(element, ion); // levels needed to get majority of ion pop @@ -1399,6 +1388,43 @@ static auto get_nlevels_important(int modelgridindex, int element, int ion, bool return nlevels_important; } +auto iongamma_is_zero(const int modelgridindex, const int element, const int ion) -> bool { + const int nions = get_nions(element); + if (ion >= nions - 1) { + return true; + } + + if constexpr (USE_LUT_PHOTOION) { + return (globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)] == 0); + } + + const auto T_e = grid::get_Te(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); + + for (int level = 0; level < get_nlevels(element, ion); level++) { + const double nnlevel = get_levelpop(modelgridindex, element, ion, level); + if (nnlevel == 0.) { + continue; + } + const int nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { + const int upperlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); + + if (nnlevel * get_corrphotoioncoeff(element, ion, level, phixstargetindex, modelgridindex) > 0.) { + return false; + } + + const double epsilon_trans = epsilon(element, ion + 1, upperlevel) - epsilon(element, ion, level); + // printout("%g %g %g\n", get_levelpop(n,element,ion,level),col_ionization(n,0,epsilon_trans),epsilon_trans); + + if (nnlevel * col_ionization_ratecoeff(T_e, nne, element, ion, level, phixstargetindex, epsilon_trans) > 0) { + return false; + } + } + } + return true; +} + auto calculate_iongamma_per_gspop(const int modelgridindex, const int element, const int ion) -> double // ionisation rate coefficient. multiply by get_groundlevelpop to get a rate [s^-1] { diff --git a/ratecoeff.h b/ratecoeff.h index edf0a9688..aa2df0d3b 100644 --- a/ratecoeff.h +++ b/ratecoeff.h @@ -3,6 +3,8 @@ void ratecoefficients_init(); +void setup_photoion_luts(void); + double select_continuum_nu(int element, int lowerion, int lower, int upperionlevel, float T_e); double interpolate_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, double T); @@ -10,9 +12,13 @@ double interpolate_corrphotoioncoeff(int element, int ion, int level, int phixst double get_spontrecombcoeff(int element, int ion, int level, int phixstargetindex, float T_e); double get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetindex, int modelgridindex); +double get_bfcoolingcoeff(int element, int ion, int level, int phixstargetindex, float T_e); + double get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, int modelgridindex); double get_corrphotoioncoeff_ana(int element, int ion, int level, int phixstargetindex, int modelgridindex); +bool iongamma_is_zero(int modelgridindex, int element, int ion); + double calculate_iongamma_per_gspop(int modelgridindex, int element, int ion); double calculate_iongamma_per_ionpop(int modelgridindex, float T_e, int element, int lowerion, bool assume_lte, bool collisional_not_radiative, bool printdebug, bool force_bfest, diff --git a/rpkt.cc b/rpkt.cc index 3909a7faf..066980e99 100644 --- a/rpkt.cc +++ b/rpkt.cc @@ -3,9 +3,9 @@ #include #include #include +#include #include "atomic.h" -#include "boundary.h" #include "grid.h" #include "kpkt.h" #include "ltepop.h" @@ -22,44 +22,35 @@ constexpr int RPKT_EVENTTYPE_BB = 550; constexpr int RPKT_EVENTTYPE_CONT = 551; -constexpr auto operator<(const linelist_entry &line, const double &nu_cmf) -> bool { return !(line.nu <= nu_cmf); } - auto closest_transition(const double nu_cmf, const int next_trans) -> int /// for the propagation through non empty cells // find the next transition lineindex redder than nu_cmf // return -1 if no transition can be reached { - const int left = next_trans; - const int right = globals::nlines - 1; - - /// if nu_cmf is smaller than the lowest frequency in the linelist, - /// no line interaction is possible: return negative value as a flag - if (nu_cmf < globals::linelist[right].nu) { + if (next_trans > (globals::nlines - 1)) { + // packet is tagged as having no more line interactions return -1; } - if (left > right) { - // printout("[debug] pp should have no line interaction anymore\n"); + /// if nu_cmf is smaller than the lowest frequency in the linelist, + /// no line interaction is possible: return negative value as a flag + if (nu_cmf < globals::linelist[globals::nlines - 1].nu) { return -1; } - if (left > 0) { + if (next_trans > 0) { /// if left = pkt_ptr->next_trans > 0 we know the next line we should interact with, independent of the packets /// current nu_cmf which might be smaller than globals::linelist[left].nu due to propagation errors - return left; + return next_trans; } - if (nu_cmf >= globals::linelist[0].nu) { - /// if nu_cmf is larger than the highest frequency in the the linelist, - /// interaction with the first line occurs - no search - return 0; - } /// otherwise go through the list until nu_cmf is located between two - /// entries in the line list and get the index of the closest line - /// to lower frequencies - // will find the highest frequency (lowest index) line with nu_line <= nu_cmf // lower_bound matches the first element where the comparison function is false - const linelist_entry *matchline = - std::lower_bound(&globals::linelist[next_trans], &globals::linelist[globals::nlines], nu_cmf); - const int matchindex = matchline - globals::linelist; + const auto *matchline = + std::lower_bound(&globals::linelist[next_trans], &globals::linelist[globals::nlines], nu_cmf, + [](const auto &line, const double nu_cmf) -> bool { return line.nu > nu_cmf; }); + const int matchindex = std::distance(globals::linelist, matchline); + if (matchindex >= globals::nlines) { + return -1; + } return matchindex; } @@ -75,9 +66,6 @@ static auto get_event(const int modelgridindex, { // printout("get_event()\n"); /// initialize loop variables - double tau = 0.; /// initial optical depth along path - double dist = 0.; /// initial position on path - double edist = 0.; struct packet dummypkt_abort = *pkt_ptr; // this is done is two parts to get identical results to do_rpkt_step() @@ -90,83 +78,39 @@ static auto get_event(const int modelgridindex, const double d_nu_on_d_l = (nu_cmf_abort - pkt_ptr->nu_cmf) / abort_dist; struct packet dummypkt = *pkt_ptr; - struct packet *const dummypkt_ptr = &dummypkt; - calculate_kappa_rpkt_cont(pkt_ptr, &globals::kappa_rpkt_cont[tid]); - const double kap_cont = globals::kappa_rpkt_cont[tid].total * doppler_packet_nucmf_on_nurf(pkt_ptr); + calculate_chi_rpkt_cont(pkt_ptr->nu_cmf, &globals::chi_rpkt_cont[tid], modelgridindex, true); + const double chi_cont = + globals::chi_rpkt_cont[tid].total * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + double tau = 0.; // optical depth along path + double dist = 0.; // position on path while (true) { /// calculate distance to next line encounter ldist /// first select the closest transition in frequency /// we need its frequency nu_trans, the element/ion and the corresponding levels /// create therefore new variables in packet, which contain next_lowerlevel, ... - const int lineindex = closest_transition(dummypkt_ptr->nu_cmf, - dummypkt_ptr->next_trans); /// returns negative value if nu_cmf > nu_trans - + const int lineindex = closest_transition(dummypkt.nu_cmf, + dummypkt.next_trans); /// returns negative value if nu_cmf > nu_trans if (lineindex >= 0) { - /// line interaction in principle possible (nu_cmf > nu_trans) - // printout("[debug] get_event: line interaction possible\n"); + /// line interaction is possible (nu_cmf > nu_trans) const double nu_trans = globals::linelist[lineindex].nu; // helper variable to overcome numerical problems after line scattering // further scattering events should be located at lower frequencies to prevent - // multiple scattering events of one pp in a single line - dummypkt_ptr->next_trans = lineindex + 1; - - double ldist = NAN; // distance from current position to the line interaction - if (dummypkt_ptr->nu_cmf <= nu_trans) { - // printout( - // "[warning] packet %d dummypkt_ptr->nu_cmf %lg <= nu_trans %lg diff %lg next_trans %d, Z=%d ionstage %d - // " "lower %d upper %d\n", pkt_ptr->number, dummypkt_ptr->nu_cmf, nu_trans, (dummypkt_ptr->nu_cmf - - // nu_trans) / nu_trans, dummypkt_ptr->next_trans, get_atomicnumber(element), get_ionstage(element, ion), - // lower, upper); - ldist = 0; /// photon was propagated too far, make sure that we don't miss a line - } else if (!USE_RELATIVISTIC_DOPPLER_SHIFT) { - ldist = CLIGHT * dummypkt_ptr->prop_time * (dummypkt_ptr->nu_cmf / nu_trans - 1); - } else { - // With special relativity, the Doppler shift formula has an extra factor of 1/gamma in it, - // which changes the distance reach a line resonance and creates a dependence - // on packet position and direction - - // relativistic distance formula from tardis-sn project - // (committed by Christian Vogl, https://github.com/tardis-sn/tardis/pull/697) - // const double nu_r = nu_trans / dummypkt_ptr->nu_rf; - // const double ct = CLIGHT * dummypkt_ptr->prop_time; - // const double r = vec_len(dummypkt_ptr->pos); // radius - // const double mu = dot(dummypkt_ptr->dir, dummypkt_ptr->pos) / r; - // ldist = -mu * r + (ct - nu_r * nu_r * sqrt(ct * ct - (1 + r * r * (1 - mu * mu) * (1 + pow(nu_r, -2))))) / - // (1 + nu_r * nu_r); - - // use linear interpolation of frequency along the path - ldist = (nu_trans - dummypkt_ptr->nu_cmf) / (nu_cmf_abort - pkt_ptr->nu_cmf) * abort_dist; - } + // multiple scattering events of one packet in a single line + dummypkt.next_trans = lineindex + 1; - if (ldist < 0.) { - printout("[warning] ldist %lg < 0.\n", ldist); - assert_always(ldist >= -100.); - ldist = 0.; - } - - // printout("[debug] get_event: ldist %g\n",ldist); + const double ldist = get_linedistance(dummypkt.prop_time, dummypkt.nu_cmf, nu_trans, d_nu_on_d_l); - const double tau_cont = kap_cont * ldist; - - // printout("[debug] get_event: tau_rnd %g, tau %g, tau_cont %g\n", tau_rnd, tau, tau_cont); + const double tau_cont = chi_cont * ldist; if (tau_rnd - tau > tau_cont) { // got past the continuum optical depth so propagate to the line, and check interaction - // if ((dist + ldist) > abort_dist) { if (nu_trans < nu_cmf_abort) { - dummypkt_ptr->next_trans -= 1; // back up one line, because we didn't reach it before the boundary/timelimit - pkt_ptr->next_trans = dummypkt_ptr->next_trans; - - // const double nextline_nu = globals::linelist[pkt_ptr->next_trans].nu; - - // printout("[debug] get_event: leave propagation loop (dist %g > abort_dist %g) ... - // dummypkt_ptr->next_trans %d\n", dist, abort_dist, dummypkt_ptr->next_trans); - - // assert_always(nextline_nu <= nu_cmf_abort); + // back up one line, because we didn't reach it before the boundary/timelimit + pkt_ptr->next_trans = dummypkt.next_trans - 1; return std::numeric_limits::max(); } @@ -182,17 +126,7 @@ static auto get_event(const int modelgridindex, const double n_u = get_levelpop(modelgridindex, element, ion, upper); const double n_l = get_levelpop(modelgridindex, element, ion, lower); - double tau_line = (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * dummypkt_ptr->prop_time; - - if (tau_line < 0) { - // printout("[warning] get_event: tau_line %g < 0, n_l %g, n_u %g, B_lu %g, B_ul %g, W %g, T_R %g, element - // %d, ion %d, upper %d, lower %d ... abort\n",tau_line, - // n_l,n_u,B_lu,B_ul,get_W(grid::get_cell_modelgridindex(pkt_ptr->where)),get_TR(grid::get_cell_modelgridindex(pkt_ptr->where)),element,ion,upper,lower); - // printout("[warning] get_event: set tau_line = 0\n"); - tau_line = 0.; - // printout("[fatal] get_event: tau_line < 0 ... abort\n"); - // abort(); - } + const double tau_line = std::max(0., (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * dummypkt.prop_time); // printout("[debug] get_event: tau_line %g\n", tau_line); // printout("[debug] get_event: tau_rnd - tau > tau_cont\n"); @@ -203,54 +137,25 @@ static auto get_event(const int modelgridindex, // printout( // "[debug] get_event: tau_rnd - tau > tau_cont + tau_line ... proceed this packets " // "propagation\n"); - // printout("[debug] get_event: dist %g, abort_dist %g, dist-abort_dist %g\n", dist, abort_dist, - // dist - abort_dist); - - dist = dist + ldist; + dist += ldist; tau += tau_cont + tau_line; - if (!USE_RELATIVISTIC_DOPPLER_SHIFT) { - move_pkt_withtime(dummypkt_ptr, ldist); + + if constexpr (!USE_RELATIVISTIC_DOPPLER_SHIFT) { + move_pkt_withtime(&dummypkt, ldist); } else { // avoid move_pkt_withtime() to skip the standard Doppler shift calculation // and use the linear approx instead - dummypkt_ptr->pos[0] += (dummypkt_ptr->dir[0] * ldist); - dummypkt_ptr->pos[1] += (dummypkt_ptr->dir[1] * ldist); - dummypkt_ptr->pos[2] += (dummypkt_ptr->dir[2] * ldist); - dummypkt_ptr->prop_time += ldist / CLIGHT_PROP; - // dummypkt_ptr->nu_cmf = nu_trans; - dummypkt_ptr->nu_cmf = pkt_ptr->nu_cmf + d_nu_on_d_l * dist; - assert_testmodeonly(dummypkt_ptr->nu_cmf <= pkt_ptr->nu_cmf); + dummypkt.pos[0] += (dummypkt.dir[0] * ldist); + dummypkt.pos[1] += (dummypkt.dir[1] * ldist); + dummypkt.pos[2] += (dummypkt.dir[2] * ldist); + dummypkt.prop_time += ldist / CLIGHT_PROP; + dummypkt.nu_cmf = pkt_ptr->nu_cmf + d_nu_on_d_l * dist; // should equal nu_trans; + assert_testmodeonly(dummypkt.nu_cmf <= pkt_ptr->nu_cmf); } - // if (fabs(dummypkt_ptr->nu_cmf / nu_trans - 1.) > 1e-5) { - // printout("dopplercheck: packet %d nu_cmf %g nu_line %g ratio-1 %g errorfrac %g\n", pkt_ptr->number, - // dummypkt_ptr->nu_cmf, nu_trans, (dummypkt_ptr->nu_cmf - nu_trans) / nu_trans, - // fabs(dummypkt_ptr->nu_cmf / nu_trans - 1.)); - // } - radfield::update_lineestimator(modelgridindex, lineindex, - dummypkt_ptr->prop_time * CLIGHT * dummypkt_ptr->e_cmf / dummypkt_ptr->nu_cmf); - - // const int next_trans = dummypkt_ptr->next_trans; - // printout( - // "[debug] get_event: dummypkt_ptr->nu_cmf %g, nu(dummypkt_ptr->next_trans=%d) %g, " - // "nu(dummypkt_ptr->next_trans-1=%d) %g\n", - // dummypkt_ptr->nu_cmf, next_trans, globals::linelist[next_trans].nu, next_trans - 1, - // globals::linelist[next_trans - 1].nu); - // printout( - // "[debug] get_event: (dummypkt_ptr->nu_cmf - " - // "nu(dummypkt_ptr->next_trans-1))/dummypkt_ptr->nu_cmf %g\n", - // (dummypkt_ptr->nu_cmf - globals::linelist[next_trans - 1].nu) / dummypkt_ptr->nu_cmf); - - // if (dummypkt_ptr->nu_cmf >= globals::linelist[next_trans].nu && - // dummypkt_ptr->nu_cmf < globals::linelist[next_trans - 1].nu) { - // printout("[debug] get_event: nu(next_trans-1) > nu_cmf >= nu(next_trans)\n"); - // } else if (dummypkt_ptr->nu_cmf < globals::linelist[next_trans].nu) { - // printout("[debug] get_event: nu_cmf < nu(next_trans)\n"); - // } else { - // printout("[debug] get_event: nu_cmf >= nu(next_trans-1)\n"); - // } + dummypkt.prop_time * CLIGHT * dummypkt.e_cmf / dummypkt.nu_cmf); } else { /// bound-bound process occurs @@ -261,27 +166,10 @@ static auto get_event(const int modelgridindex, pkt_ptr->mastate.level = upper; /// if the MA will be activated it must be in the transitions upper level pkt_ptr->mastate.activatingline = lineindex; - edist = dist + ldist; - if (edist >= abort_dist) { - // if the edist > abort_dist, the line will not be activated in do_rpkt, even thought we are sure that we - // should hit based on the frequency checks - // this seems to only occur for kilonova models, maybe due to some combination of: - // - very rapid expansion - // - relativistic doppler shift (more complex expression causing numerical errors) - // - massive atomic dataset with densely packed lines - const double edist_new = abort_dist * (1 - 2e-8); - printout( - "[warning] bound-bound edist %lg was >= abort_dist %lg but nu_trans >= nu_cmf_abort (we haven't " - "redshifted past abort boundary). Fixing by reducing event distance to %lg ...\n", - edist, abort_dist, edist_new); - edist = edist_new; - } - - if (DETAILED_LINE_ESTIMATORS_ON) { - move_pkt_withtime(dummypkt_ptr, ldist); - radfield::update_lineestimator( - modelgridindex, lineindex, - dummypkt_ptr->prop_time * CLIGHT * dummypkt_ptr->e_cmf / dummypkt_ptr->nu_cmf); + if constexpr (DETAILED_LINE_ESTIMATORS_ON) { + move_pkt_withtime(&dummypkt, ldist); + radfield::update_lineestimator(modelgridindex, lineindex, + dummypkt.prop_time * CLIGHT * dummypkt.e_cmf / dummypkt.nu_cmf); } *rpkt_eventtype = RPKT_EVENTTYPE_BB; @@ -289,64 +177,46 @@ static auto get_event(const int modelgridindex, // printout("[debug] get_event: edist %g, abort_dist %g, edist-abort_dist %g, endloop // %d\n",edist,abort_dist,edist-abort_dist,endloop); - pkt_ptr->next_trans = dummypkt_ptr->next_trans; + pkt_ptr->next_trans = dummypkt.next_trans; - return edist; + return dist + ldist; } } else { - /// continuum process occurs - - edist = dist + (tau_rnd - tau) / kap_cont; - // assert_always((tau_rnd - tau) / kap_cont < ldist); - dummypkt_ptr->next_trans -= 1; - // printout("[debug] get_event: distance to the occuring continuum event %g, abort_dist %g\n", edist, - // abort_dist); + /// continuum process occurs before reaching the line *rpkt_eventtype = RPKT_EVENTTYPE_CONT; - pkt_ptr->next_trans = dummypkt_ptr->next_trans; + pkt_ptr->next_trans = dummypkt.next_trans - 1; - return edist; + return dist + (tau_rnd - tau) / chi_cont; } } else { /// no line interaction possible - check whether continuum process occurs in cell - // printout("[debug] get_event: line interaction impossible\n"); - - /// helper variable to overcome numerical problems after line scattering - dummypkt_ptr->next_trans = globals::nlines + 1; - - const double tau_cont = kap_cont * (abort_dist - dist); + const double tau_cont = chi_cont * (abort_dist - dist); if (tau_rnd - tau > tau_cont) { - /// travel out of cell or time step - // printout("[debug] get_event: travel out of cell or time step\n"); - - edist = std::numeric_limits::max(); - } else { - /// continuum process occurs at edist - edist = dist + (tau_rnd - tau) / kap_cont; - // printout("[debug] get_event: continuum process occurs at edist %g\n",edist); - - *rpkt_eventtype = RPKT_EVENTTYPE_CONT; + // no continuum event before abort_dist + return std::numeric_limits::max(); } + /// continuum process occurs at edist - pkt_ptr->next_trans = dummypkt_ptr->next_trans; + *rpkt_eventtype = RPKT_EVENTTYPE_CONT; - return edist; + pkt_ptr->next_trans = globals::nlines + 1; + + return dist + (tau_rnd - tau) / chi_cont; } } // should have already returned somewhere! assert_always(false); - - return edist; } static void electron_scatter_rpkt(struct packet *pkt_ptr) { /// now make the packet a r-pkt and set further flags pkt_ptr->type = TYPE_RPKT; - pkt_ptr->last_cross = NONE; /// allow all further cell crossings + pkt_ptr->last_cross = BOUNDARY_NONE; /// allow all further cell crossings double vel_vec[3]; get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); @@ -426,10 +296,10 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { double ref2[3]; meridian(old_dir_cmf, ref1, ref2); - /* This is the i1 angle of Bulla+2015, obtained by computing the angle between the - reference axes ref1 and ref2 in the meridian frame and the corresponding axes - ref1_sc and ref2_sc in the scattering plane. It is the supplementary angle of the - scatt angle phisc chosen in the rejection technique above (phisc+i1=180 or phisc+i1=540) */ + // This is the i1 angle of Bulla+2015, obtained by computing the angle between the + // reference axes ref1 and ref2 in the meridian frame and the corresponding axes + // ref1_sc and ref2_sc in the scattering plane. It is the supplementary angle of the + // scatt angle phisc chosen in the rejection technique above (phisc+i1=180 or phisc+i1=540) const double i1 = rot_angle(old_dir_cmf, new_dir_cmf, ref1, ref2); const double cos2i1 = cos(2 * i1); const double sin2i1 = sin(2 * i1); @@ -454,8 +324,8 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { meridian(new_dir_cmf, ref1, ref2); // This is the i2 angle of Bulla+2015, obtained from the angle THETA between the - // reference axes ref1_sc and ref2_sc in the scattering plane and ref1 and ref2 in the - // meridian frame. NB: we need to add PI to transform THETA to i2 + // reference axes ref1_sc and ref2_sc in the scattering plane and ref1 and ref2 in the + // meridian frame. NB: we need to add PI to transform THETA to i2 const double i2 = PI + rot_angle(new_dir_cmf, old_dir_cmf, ref1, ref2); const double cos2i2 = cos(2 * i2); const double sin2i2 = sin(2 * i2); @@ -469,48 +339,48 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { vel_rev[1] = -vel_vec[1]; vel_rev[2] = -vel_vec[2]; - double dummy_dir[3]; + double dummy_dir[3] = {NAN, NAN, NAN}; frame_transform(new_dir_cmf, &Q, &U, vel_rev, dummy_dir); pkt_ptr->stokes[0] = I; pkt_ptr->stokes[1] = Q; pkt_ptr->stokes[2] = U; - // ---------------------- Update rest frame direction, frequency and energy -------------------- + // Update rest frame direction, frequency and energy pkt_ptr->dir[0] = dummy_dir[0]; pkt_ptr->dir[1] = dummy_dir[1]; pkt_ptr->dir[2] = dummy_dir[2]; - // Check unit vector. + // Check unit vector assert_testmodeonly(fabs(vec_len(pkt_ptr->dir) - 1.) < 1.e-6); // Finally we want to put in the rest frame energy and frequency. // And record that it's now a r-pkt. - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_rf = pkt_ptr->nu_cmf / dopplerfactor; pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; } -static void rpkt_event_continuum(struct packet *pkt_ptr, struct rpkt_cont_opacity kappa_rpkt_cont_thisthread, - int modelgridindex) { +static void rpkt_event_continuum(struct packet *pkt_ptr, + struct rpkt_continuum_absorptioncoeffs chi_rpkt_cont_thisthread, int modelgridindex) { const double nu = pkt_ptr->nu_cmf; - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); - const double kappa_cont = kappa_rpkt_cont_thisthread.total * dopplerfactor; - const double sigma = kappa_rpkt_cont_thisthread.es * dopplerfactor; - const double kappa_ff = kappa_rpkt_cont_thisthread.ff * dopplerfactor; - const double kappa_bf = kappa_rpkt_cont_thisthread.bf * dopplerfactor; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + const double chi_cont = chi_rpkt_cont_thisthread.total * dopplerfactor; + const double sigma = chi_rpkt_cont_thisthread.es * dopplerfactor; + const double chi_ff = chi_rpkt_cont_thisthread.ff * dopplerfactor; + const double chi_bf = chi_rpkt_cont_thisthread.bf * dopplerfactor; - /// continuum process happens. select due to its probabilities sigma/kappa_cont, kappa_ff/kappa_cont, - /// kappa_bf/kappa_cont + /// continuum process happens. select due to its probabilities sigma/chi_cont, chi_ff/chi_cont, + /// chi_bf/chi_cont const double zrand = rng_uniform(); // printout("[debug] rpkt_event: r-pkt undergoes a continuum transition\n"); - // printout("[debug] rpkt_event: zrand*kappa_cont %g, sigma %g, kappa_ff %g, kappa_bf %g\n", zrand * kappa_cont, - // sigma, kappa_ff, kappa_bf); + // printout("[debug] rpkt_event: zrand*chi_cont %g, sigma %g, chi_ff %g, chi_bf %g\n", zrand * chi_cont, + // sigma, chi_ff, chi_bf); - if (zrand * kappa_cont < sigma) { + if (zrand * chi_cont < sigma) { /// electron scattering occurs /// in this case the packet stays a R_PKT of same nu_cmf than before (coherent scattering) /// but with different direction @@ -521,11 +391,7 @@ static void rpkt_event_continuum(struct packet *pkt_ptr, struct rpkt_cont_opacit stats::increment(stats::COUNTER_ESCOUNTER); // generate a virtual packet - if constexpr (VPKT_ON) { - int const realtype = 1; - pkt_ptr->last_cross = NONE; - vpkt_call_estimators(pkt_ptr, pkt_ptr->prop_time, realtype); - } + vpkt_call_estimators(pkt_ptr, TYPE_RPKT); // pkt_ptr->nu_cmf = 3.7474058e+14; electron_scatter_rpkt(pkt_ptr); @@ -538,7 +404,7 @@ static void rpkt_event_continuum(struct packet *pkt_ptr, struct rpkt_cont_opacit /// Set some flags // pkt_ptr->next_trans = 0; ///packet's comoving frame frequency is conserved during electron scattering /// don't touch the value of next_trans to save transition history - } else if (zrand * kappa_cont < sigma + kappa_ff) { + } else if (zrand * chi_cont < sigma + chi_ff) { /// ff: transform to k-pkt // printout("[debug] rpkt_event: free-free transition\n"); stats::increment(stats::COUNTER_K_STAT_FROM_FF); @@ -546,23 +412,23 @@ static void rpkt_event_continuum(struct packet *pkt_ptr, struct rpkt_cont_opacit pkt_ptr->last_event = 5; pkt_ptr->type = TYPE_KPKT; pkt_ptr->absorptiontype = -1; - } else if (zrand * kappa_cont < sigma + kappa_ff + kappa_bf) { + } else if (zrand * chi_cont < sigma + chi_ff + chi_bf) { /// bf: transform to k-pkt or activate macroatom corresponding to probabilities // printout("[debug] rpkt_event: bound-free transition\n"); pkt_ptr->absorptiontype = -2; - const double kappa_bf_inrest = kappa_rpkt_cont_thisthread.bf; - assert_always(globals::phixslist[tid].kappa_bf_sum[globals::nbfcontinua - 1] == kappa_bf_inrest); + const double chi_bf_inrest = chi_rpkt_cont_thisthread.bf; + assert_always(globals::phixslist[tid].chi_bf_sum[globals::nbfcontinua - 1] == chi_bf_inrest); /// Determine in which continuum the bf-absorption occurs const double zrand2 = rng_uniform(); - const double kappa_bf_rand = zrand2 * kappa_bf_inrest; + const double chi_bf_rand = zrand2 * chi_bf_inrest; - // first kappa_bf_sum[i] such that kappa_bf_sum[i] > kappa_bf_rand - double *upperval = std::upper_bound(&globals::phixslist[tid].kappa_bf_sum[0], - &globals::phixslist[tid].kappa_bf_sum[globals::nbfcontinua - 1], kappa_bf_rand); - const int allcontindex = std::distance(&globals::phixslist[tid].kappa_bf_sum[0], upperval); + // first chi_bf_sum[i] such that chi_bf_sum[i] > chi_bf_rand + double *upperval = std::upper_bound(&globals::phixslist[tid].chi_bf_sum[0], + &globals::phixslist[tid].chi_bf_sum[globals::nbfcontinua - 1], chi_bf_rand); + const int allcontindex = std::distance(&globals::phixslist[tid].chi_bf_sum[0], upperval); assert_always(allcontindex < globals::nbfcontinua); const double nu_edge = globals::allcont[allcontindex].nu_edge; @@ -661,7 +527,7 @@ static void rpkt_event_thickcell(struct packet *pkt_ptr) pkt_ptr->last_event = 12; stats::increment(stats::COUNTER_ESCOUNTER); - emitt_rpkt(pkt_ptr); + emit_rpkt(pkt_ptr); /// Electron scattering does not modify the last emission flag // pkt_ptr->emissiontype = get_continuumindex(element,ion-1,lower); /// but it updates the last emission position @@ -669,59 +535,12 @@ static void rpkt_event_thickcell(struct packet *pkt_ptr) pkt_ptr->em_time = pkt_ptr->prop_time; } -static void closest_transition_empty(struct packet *pkt_ptr) -/// for the propagation through empty cells -/// here its possible that the packet jumps over several lines -{ - // int left = 0; - int const left = pkt_ptr->next_trans; - // printout("[debug] closest_transition: initial left %d\n",left); - int const right = globals::nlines - 1; - - // printout("[debug] ___closest_transition___: initial left %d, right %d, nu_cmf %g\n",left,right,pkt_ptr->nu_cmf); - // printout("[debug] ___closest_transition___: nu_left %g, nu_right%g\n",linelist[left].nu,linelist[right].nu); - /// if nu_cmf is smaller than the lowest frequency in the linelist, - /// no line interaction is possible: return negative value as a flag - if (pkt_ptr->nu_cmf < globals::linelist[right].nu) { - pkt_ptr->next_trans = globals::nlines + 1; /// helper variable to overcome numerical problems after line scattering - } - if (left > right) { - // printout("[debug] pp should have no line interaction anymore\n"); - pkt_ptr->next_trans = globals::nlines + 1; /// helper variable to overcome numerical problems after line scattering - } - - int matchindex = 0; - /// no check for left > 0 in the empty case as it is possible that the packet is moved over - /// several lines through the empty cell - if (pkt_ptr->nu_cmf >= globals::linelist[left].nu) { - /// if nu_cmf is larger than the highest frequency in the allowed part of the linelist, - /// interaction with the first line of this part of the list occurs - matchindex = left; - } else { - /// otherwise go through the list until nu_cmf is located between two - /// entries in the line list and get the index of the closest line - /// to lower frequencies - - const linelist_entry *matchline = - std::lower_bound(&globals::linelist[pkt_ptr->next_trans], &globals::linelist[globals::nlines], pkt_ptr->nu_cmf); - matchindex = matchline - globals::linelist; - } - - /// For the empty case it's match not match+1: a line interaction is only possible in the next iteration - /// of the propagation loop. We just have to make sure that the next "normal" line search knows about the - /// current position of the photon in the frequency list. - pkt_ptr->next_trans = matchindex; /// helper variable to overcome numerical problems after line scattering - /// further scattering events should be located at lower frequencies to prevent - /// multiple scattering events of one pp in a single line -} - -static void update_estimators(struct packet *pkt_ptr, const double distance) +static void update_estimators(const struct packet *pkt_ptr, const double distance) /// Update the volume estimators J and nuJ /// This is done in another routine than move, as we sometimes move dummy /// packets which do not contribute to the radiation field. { - const int cellindex = pkt_ptr->where; - const int modelgridindex = grid::get_cell_modelgridindex(cellindex); + const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); /// Update only non-empty cells if (modelgridindex == grid::get_npts_model()) { @@ -733,7 +552,7 @@ static void update_estimators(struct packet *pkt_ptr, const double distance) /// ffheatingestimator does not depend on ion and element, so an array with gridsize is enough. /// quick and dirty solution: store info in element=ion=0, and leave the others untouched (i.e. zero) - safeadd(globals::ffheatingestimator[modelgridindex], distance_e_cmf * globals::kappa_rpkt_cont[tid].ffheating); + safeadd(globals::ffheatingestimator[modelgridindex], distance_e_cmf * globals::chi_rpkt_cont[tid].ffheating); if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { const double distance_e_cmf_over_nu = distance_e_cmf / nu; @@ -745,7 +564,7 @@ static void update_estimators(struct packet *pkt_ptr, const double distance) if (nu > nu_edge) { const int element = globals::groundcont[i].element; /// Cells with zero abundance for a specific element have zero contribution - /// (set in calculate_kappa_rpkt_cont and therefore do not contribute to + /// (set in calculate_chi_rpkt_cont and therefore do not contribute to /// the estimators if (grid::get_elem_abundance(modelgridindex, element) > 0) { const int ion = globals::groundcont[i].ion; @@ -771,7 +590,7 @@ static void update_estimators(struct packet *pkt_ptr, const double distance) } } } else { - break; // because groundcont is sorted by nu_edge, nu < nu_edge for all remaining items + break; // because groundcont is sorted by nu_edge descending, nu < nu_edge for all remaining items } } } @@ -801,18 +620,19 @@ static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool // boundaries. sdist is the boundary distance and snext is the // grid cell into which we pass. int snext = 0; - double sdist = boundary_cross(pkt_ptr, &snext); + double sdist = grid::boundary_distance(pkt_ptr->dir, pkt_ptr->pos, pkt_ptr->prop_time, pkt_ptr->where, &snext, + &pkt_ptr->last_cross); if (sdist == 0) { - change_cell(pkt_ptr, snext); + grid::change_cell(pkt_ptr, snext); const int cellindexnew = pkt_ptr->where; mgi = grid::get_cell_modelgridindex(cellindexnew); return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); } - const double maxsdist = (GRID_TYPE == GRID_SPHERICAL1D) - ? 2 * globals::rmax * (pkt_ptr->prop_time + sdist / CLIGHT_PROP) / globals::tmin - : globals::rmax * pkt_ptr->prop_time / globals::tmin; + const double maxsdist = (GRID_TYPE == GRID_CARTESIAN3D) + ? globals::rmax * pkt_ptr->prop_time / globals::tmin + : 2 * globals::rmax * (pkt_ptr->prop_time + sdist / CLIGHT_PROP) / globals::tmin; if (sdist > maxsdist) { printout("[fatal] do_rpkt: Unreasonably large sdist for packet %d. Rpkt. Abort. %g %g %g\n", pkt_ptr->number, globals::rmax, pkt_ptr->prop_time / globals::tmin, sdist); @@ -829,8 +649,8 @@ static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool grid::get_cellcoordmin(cellindexnew, 0) * pkt_ptr->prop_time / globals::tmin, grid::get_cellcoordmin(cellindexnew, 1) * pkt_ptr->prop_time / globals::tmin, grid::get_cellcoordmin(cellindexnew, 2) * pkt_ptr->prop_time / globals::tmin); - printout("[warning] r_pkt: cell width %g\n", grid::wid_init(0) * pkt_ptr->prop_time / globals::tmin); - // abort(); + printout("[warning] r_pkt: cell width %g\n", grid::wid_init(cellindexnew, 0) * pkt_ptr->prop_time / globals::tmin); + assert_always(false); } if (((snext != -99) && (snext < 0)) || (snext >= grid::ngrid)) { printout("[fatal] r_pkt: Heading for inappropriate grid cell. Abort.\n"); @@ -848,71 +668,49 @@ static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool // Find how far it can travel during the time inverval. - double const tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; + const double tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; assert_always(tdist >= 0); - double edist = NAN; + /// Get distance to the next physical event (continuum or bound-bound) + double edist = -1; int rpkt_eventtype = -1; - bool find_nextline = false; if (mgi == grid::get_npts_model()) { /// for empty cells no physical event occurs. The packets just propagate. edist = std::numeric_limits::max(); - find_nextline = true; - // printout("[debug] do_rpkt: propagating through empty cell, set edist=1e99\n"); + pkt_ptr->next_trans = -1; // skip over lines and search for line list position on the next non-empty cell } else if (grid::modelgrid[mgi].thick == 1) { /// In the case of optically thick cells, we treat the packets in grey approximation to speed up the calculation - /// Get distance to the next physical event in this case only electron scattering - // kappa = SIGMA_T*grid::get_nne(mgi); - const double kappa = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * doppler_packet_nucmf_on_nurf(pkt_ptr); + + const double kappa = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * + doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); const double tau_current = 0.0; edist = (tau_next - tau_current) / kappa; - find_nextline = true; - // printout("[debug] do_rpkt: propagating through grey cell, edist %g\n",edist); + pkt_ptr->next_trans = -1; } else { - // get distance to the next physical event (continuum or bound-bound) - edist = get_event(mgi, pkt_ptr, &rpkt_eventtype, tau_next, - fmin(tdist, sdist)); //, kappacont_ptr, sigma_ptr, kappaff_ptr, kappabf_ptr); - - // const int next_trans = pkt_ptr->next_trans; - // printout("[debug] do_rpkt: after edist: pkt_ptr->nu_cmf %g, nu(pkt_ptr->next_trans=%d) %g\n", pkt_ptr->nu_cmf, - // next_trans, globals::linelist[next_trans].nu); + edist = get_event(mgi, pkt_ptr, &rpkt_eventtype, tau_next, fmin(tdist, sdist)); } assert_always(edist >= 0); - // printout("[debug] do_rpkt: packet %d sdist, tdist, edist %g, %g, %g old_last_cross %d next_cross %d cellindex - // %d dir %g %g - // %g\n",pkt_ptr->number,sdist,tdist,edist,old_last_cross,pkt_ptr->last_cross,pkt_ptr->where,pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); - if ((sdist < tdist) && (sdist < edist)) { - // printout("[debug] do_rpkt: sdist < tdist && sdist < edist\n"); // Move it into the new cell. move_pkt_withtime(pkt_ptr, sdist / 2.); update_estimators(pkt_ptr, sdist); move_pkt_withtime(pkt_ptr, sdist / 2.); if (snext != pkt_ptr->where) { - change_cell(pkt_ptr, snext); + grid::change_cell(pkt_ptr, snext); const int cellindexnew = pkt_ptr->where; mgi = grid::get_cell_modelgridindex(cellindexnew); } pkt_ptr->last_event = pkt_ptr->last_event + 100; - /// For empty or grey cells a photon can travel over several bb-lines. Thus we need to - /// find the next possible line interaction. - if (find_nextline) { - /// However, this is only required if the new cell is non-empty or non-grey - if (mgi != grid::get_npts_model() && grid::modelgrid[mgi].thick != 1) { - closest_transition_empty(pkt_ptr); - } - } - return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); } + if ((edist < sdist) && (edist < tdist)) { // bound-bound or continuum event - // printout("[debug] do_rpkt: edist < sdist && edist < tdist\n"); move_pkt_withtime(pkt_ptr, edist / 2.); update_estimators(pkt_ptr, edist); move_pkt_withtime(pkt_ptr, edist / 2.); @@ -923,30 +721,25 @@ static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool } else if (rpkt_eventtype == RPKT_EVENTTYPE_BB) { rpkt_event_boundbound(pkt_ptr, mgi); } else if (rpkt_eventtype == RPKT_EVENTTYPE_CONT) { - rpkt_event_continuum(pkt_ptr, globals::kappa_rpkt_cont[tid], mgi); + rpkt_event_continuum(pkt_ptr, globals::chi_rpkt_cont[tid], mgi); } else { assert_always(false); } return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); } + if ((tdist < sdist) && (tdist < edist)) { // reaches end of timestep before cell boundary or interaction - // printout("[debug] do_rpkt: tdist < sdist && tdist < edist\n"); move_pkt_withtime(pkt_ptr, tdist / 2.); update_estimators(pkt_ptr, tdist); pkt_ptr->prop_time = t2; move_pkt(pkt_ptr, tdist / 2.); pkt_ptr->last_event = pkt_ptr->last_event + 1000; - /// For empty or grey cells a photon can travel over several bb-lines. Thus we need to - /// find the next possible line interaction. - if (find_nextline) { - closest_transition_empty(pkt_ptr); - } - return false; } + printout("[fatal] do_rpkt: Failed to identify event . Rpkt. edist %g, sdist %g, tdist %g Abort.\n", edist, sdist, tdist); printout("[fatal] do_rpkt: Trouble was due to packet number %d.\n", pkt_ptr->number); @@ -959,163 +752,10 @@ void do_rpkt(struct packet *pkt_ptr, const double t2) { } } -static auto get_rpkt_escapeprob_fromdirection(const double startpos[3], double start_nu_cmf, int startcellindex, - double tstart, double dirvec[3], enum cell_boundary last_cross, - double *tot_tau_cont, double *tot_tau_lines) -> double { - struct packet vpkt; - vpkt.type = TYPE_RPKT; - vpkt.nu_cmf = start_nu_cmf; - vpkt.where = startcellindex; - vpkt.next_trans = 0; - vpkt.last_cross = last_cross; - - vec_copy(vpkt.dir, dirvec); - vec_copy(vpkt.pos, startpos); - - vpkt.prop_time = tstart; - const double dopplerfactor = doppler_packet_nucmf_on_nurf(&vpkt); - vpkt.nu_rf = vpkt.nu_cmf / dopplerfactor; - - double t_future = tstart; - - int snext = -99; - bool end_packet = false; - while (!end_packet) { - const int cellindex = vpkt.where; - const int mgi = grid::get_cell_modelgridindex(cellindex); - if (grid::modelgrid[mgi].thick == 1) { - return 0.; - } - - // distance to the next cell - vpkt.prop_time = t_future; - const double sdist = boundary_cross(&vpkt, &snext); - - if (snext >= 0) { - const int nextmgi = grid::get_cell_modelgridindex(snext); - if (grid::modelgrid[nextmgi].thick == 1) { - return 0.; - } - } - - calculate_kappa_rpkt_cont(&vpkt, &globals::kappa_rpkt_cont[tid]); - - const double kappa_cont = globals::kappa_rpkt_cont[tid].total * doppler_packet_nucmf_on_nurf(&vpkt); - - *tot_tau_cont += kappa_cont * sdist; - - if ((*tot_tau_lines + *tot_tau_cont) > 10.) { - // printout("reached tau limit of %g\n", (tot_tau_lines + tot_tau_cont)); - return 0.; - } - - double ldist = 0.; - while (ldist < sdist) { - const int lineindex = closest_transition(vpkt.nu_cmf, vpkt.next_trans); - - if (lineindex >= 0) { - const double nutrans = globals::linelist[lineindex].nu; - - vpkt.next_trans = lineindex + 1; - - if (vpkt.nu_cmf < nutrans) { - ldist = 0; - } else { - ldist = CLIGHT * t_future * (vpkt.nu_cmf / nutrans - 1); - } - - assert_always(ldist >= 0.); - - if (ldist > sdist) { - // exit the while loop if you reach the boundary; go back to the previous transition to start next cell with - // the excluded line - - vpkt.next_trans -= 1; - break; - } - - const double t_line = t_future + ldist / CLIGHT; - const double tau_line = get_tau_sobolev(mgi, lineindex, t_line); - - *tot_tau_lines += tau_line; - } else { - vpkt.next_trans = globals::nlines + 1; - break; - } - } - - if (snext < 0 || grid::get_cell_modelgridindex(snext) == grid::get_npts_model()) { - break; - } - - t_future += (sdist / CLIGHT_PROP); - vpkt.prop_time = t_future; - move_pkt(&vpkt, sdist); - - if (snext != vpkt.where) { - vpkt.prop_time = t_future; - change_cell(&vpkt, snext); - end_packet = (vpkt.type == TYPE_ESCAPE); - } - } - - const double tau_escape = *tot_tau_cont + *tot_tau_lines; - const double escape_prob = exp(-tau_escape); - // printout(" tot_tau_lines %g tot_tau_cont %g escape_prob %g\n", - // tot_tau_lines, tot_tau_cont, escape_prob); - return escape_prob; -} - -auto get_rpkt_escape_prob(struct packet *pkt_ptr, const double tstart) -> double { - // return -1.; // disable this functionality and speed up the code - - const int startcellindex = pkt_ptr->where; - double startpos[3]; - vec_copy(startpos, pkt_ptr->pos); - const double start_nu_cmf = pkt_ptr->nu_cmf; - const enum cell_boundary last_cross = pkt_ptr->last_cross; - const int mgi = grid::get_cell_modelgridindex(startcellindex); - if (grid::modelgrid[mgi].thick == 1) { - // escape prob in thick cell is zero - return 0.; - } - const time_t sys_time_start_escape_prob = time(nullptr); - - const double pkt_radius = vec_len(startpos); - const double rmaxnow = globals::rmax * tstart / globals::tmin; - printout("get_rpkt_escape_prob pkt_radius %g rmax %g r/rmax %g tstart %g\n", pkt_radius, rmaxnow, - pkt_radius / rmaxnow, tstart); - // assert_always(pkt_radius <= rmaxnow); - double escape_prob_sum = 0.; - const int ndirs = 40; // number of random directions to sample - for (int n = 0; n < ndirs; n++) { - double dirvec[3]; - get_rand_isotropic_unitvec(dirvec); - double tau_cont = 0.; - double tau_lines = 0.; - const double escape_prob = get_rpkt_escapeprob_fromdirection(startpos, start_nu_cmf, startcellindex, tstart, dirvec, - last_cross, &tau_cont, &tau_lines); - escape_prob_sum += escape_prob; - - printout( - "randomdir no. %d (dir dot pos) %g dir %g %g %g tau_lines %g tau_cont %g escape_prob %g escape_prob_avg %g\n", - n, dot(startpos, dirvec), dirvec[0], dirvec[1], dirvec[2], tau_cont, tau_lines, escape_prob, - escape_prob_sum / (n + 1)); - } - const double escape_prob_avg = escape_prob_sum / ndirs; - printout("from %d random directions, average escape probability is %g (took %ld s)\n", ndirs, escape_prob_avg, - time(nullptr) - sys_time_start_escape_prob); - - // reset the cell history and rpkt opacities back to values for the start point - cellhistory_reset(mgi, false); - - return escape_prob_avg; -} - -void emitt_rpkt(struct packet *pkt_ptr) { +void emit_rpkt(struct packet *pkt_ptr) { /// now make the packet a r-pkt and set further flags pkt_ptr->type = TYPE_RPKT; - pkt_ptr->last_cross = NONE; /// allow all further cell crossings + pkt_ptr->last_cross = BOUNDARY_NONE; /// allow all further cell crossings /// Need to assign a new direction. Assume isotropic emission in the cmf @@ -1135,7 +775,7 @@ void emitt_rpkt(struct packet *pkt_ptr) { /// Finally we want to put in the rest frame energy and frequency. And record /// that it's now a r-pkt. - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_rf = pkt_ptr->nu_cmf / dopplerfactor; pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; @@ -1144,14 +784,11 @@ void emitt_rpkt(struct packet *pkt_ptr) { pkt_ptr->stokes[1] = 0.; pkt_ptr->stokes[2] = 0.; - double dummy_dir[3]; - dummy_dir[0] = dummy_dir[1] = 0.0; - dummy_dir[2] = 1.0; + std::array dummy_dir = {0., 0., 1.}; cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); if ((dot(pkt_ptr->pol_dir, pkt_ptr->pol_dir)) < 1.e-8) { - dummy_dir[0] = dummy_dir[2] = 0.0; - dummy_dir[1] = 1.0; + dummy_dir = {0., 0., 1.}; cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); } @@ -1161,67 +798,50 @@ void emitt_rpkt(struct packet *pkt_ptr) { // printout("pkt direction %g, %g, %g\n",pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); } -static auto calculate_kappa_ff(const int modelgridindex, const double nu) -> double -/// free-free opacity +static auto calculate_chi_ff(const int modelgridindex, const double nu) -> double +// calculate the free-free absorption coefficient [cm^-1] +// = kappa(free-free) * nne { assert_always(nu > 0.); const double g_ff = 1; - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); const auto T_e = grid::get_Te(modelgridindex); - double kappa_ff = 0.; - // kappa_ffheating = 0.; + double chi_ff = 0.; + // chi_ffheating = 0.; const int nelements = get_nelements(); for (int element = 0; element < nelements; element++) { for (int ion = 0; ion < get_nions(element); ion++) { - /// calculate population of ionstage ... - const double nnion = ionstagepop(modelgridindex, element, ion); - // Z = get_atomicnumber(element); ///atomic number - // if (get_ionstage(element,ion) > 1) - /// Z is ionic charge in the following formula - const int Z = get_ionstage(element, ion) - 1; - if (Z > 0) { - // kappa_ff += 3.69255e8 * pow(Z,2) / sqrt(T_e) * pow(nu,-3) * g_ff * nne * nnion * (1-exp(-HOVERKB*nu/T_e)); - kappa_ff += Z * Z * g_ff * nnion; - // kappa_ffheating += pow(Z,2) * g_ff * nnion; - /// heating with level dependence - // kappa_ffheating += 3.69255e8 * pow(Z,2) / sqrt(T_e) * pow(nu,-3) * g_ff * nne * nnion * (1 - - // exp(-HOVERKB*nu/T_e)); - /// heating without level dependence - // kappa_ffheating += 3.69255e8 * pow(Z,2) * pow(nu,-3) * g_ff * (1-exp(-HOVERKB*nu/T_e)); - // if (!std::isfinite(kappa_ff)) { - // printout("kappa_ff %g nne %g T_e %g mgi %d element %d ion %d nnion %g\n", kappa_ff, nne, T_e, - // modelgridindex, - // element, ion, nnion); - // } - } + const double nnion = get_nnion(modelgridindex, element, ion); + const int ioncharge = get_ionstage(element, ion) - 1; + chi_ff += ioncharge * ioncharge * g_ff * nnion; } } - kappa_ff *= 3.69255e8 / sqrt(T_e) * pow(nu, -3) * nne * (1 - exp(-HOVERKB * nu / T_e)); + chi_ff *= 3.69255e8 / sqrt(T_e) * pow(nu, -3) * nne * (1 - exp(-HOVERKB * nu / T_e)); - if (!std::isfinite(kappa_ff)) { - printout("ERRORL: kappa_ff is non-infinite mgi %d nne %g nu %g T_e %g\n", modelgridindex, nne, nu, T_e); + if (!std::isfinite(chi_ff)) { + printout("ERRORL: chi_ff is non-infinite mgi %d nne %g nu %g T_e %g\n", modelgridindex, nne, nu, T_e); abort(); } - // kappa_ffheating *= 3.69255e8 / sqrt(T_e) * pow(nu,-3) * nne * (1 - exp(-HOVERKB*nu/T_e)); - // kappa_ff *= 1e5; - return kappa_ff; + + return chi_ff; } -auto calculate_kappa_bf_gammacontr(const int modelgridindex, const double nu) -> double +template +auto calculate_chi_bf_gammacontr(const int modelgridindex, const double nu) -> double // bound-free opacity { - double kappa_bf_sum = 0.; - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { + double chi_bf_sum = 0.; + if constexpr (usecellhistupdatephixslist && (USE_LUT_PHOTOION || USE_LUT_BFHEATING)) { for (int gphixsindex = 0; gphixsindex < globals::nbfcontinua_ground; gphixsindex++) { globals::phixslist[tid].groundcont_gamma_contr[gphixsindex] = 0.; } } - const double T_e = grid::get_Te(modelgridindex); - const double nne = grid::get_nne(modelgridindex); - const double nnetot = grid::get_nnetot(modelgridindex); + const auto T_e = grid::get_Te(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); + const auto nnetot = grid::get_nnetot(modelgridindex); /// The phixslist is sorted by nu_edge in ascending order (longest to shortest wavelength) /// If nu < allcont[i].nu_edge no absorption in any of the following continua @@ -1243,67 +863,56 @@ auto calculate_kappa_bf_gammacontr(const int modelgridindex, const double nu) -> /// the involved atomic species if ((DETAILED_BF_ESTIMATORS_ON && grid::get_elem_abundance(modelgridindex, element) > 0) || - (!DETAILED_BF_ESTIMATORS_ON && - ((ionstagepop(modelgridindex, element, ion) / nnetot > 1.e-6) || (level == 0)))) { + (!DETAILED_BF_ESTIMATORS_ON && ((get_nnion(modelgridindex, element, ion) / nnetot > 1.e-6) || (level == 0)))) { const double nu_edge = globals::allcont[i].nu_edge; - // const int phixstargetindex = globals::allcont[i].phixstargetindex; - const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - // printout("i %d, nu_edge %g\n",i,nu_edge); - const double nu_max_phixs = nu_edge * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table if (nu < nu_edge) { break; } + const double nnlevel = usecellhistupdatephixslist ? get_levelpop(modelgridindex, element, ion, level) + : calculate_levelpop(modelgridindex, element, ion, level); + const double nu_max_phixs = nu_edge * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table if (nu <= nu_max_phixs && nnlevel > 0) { - // printout("element %d, ion %d, level %d, nnlevel %g\n",element,ion,level,nnlevel); - // const double sigma_bf = photoionization_crosssection(element, ion, level, nu_edge, nu); - // const double sigma_bf = photoionization_crosssection_fromtable( - // globals::elements[element].ions[ion].levels[level].photoion_xs, nu_edge, nu); const double sigma_bf = photoionization_crosssection_fromtable(globals::allcont[i].photoion_xs, nu_edge, nu); - // const double probability = get_phixsprobability(element, ion, level, phixstargetindex); const double probability = globals::allcont[i].probability; - // assert_always(probability == probability2); - double corrfactor = NAN; - if constexpr (SEPARATE_STIMRECOMB) { - corrfactor = 1.; // no subtraction of stimulated recombination - } else { + double corrfactor = 1.; // default to no subtraction of stimulated recombination + if constexpr (!SEPARATE_STIMRECOMB) { double departure_ratio = globals::cellhistory[tid].ch_allcont_departureratios[i]; - if (departure_ratio < 0) { - // const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); - + if (!usecellhistupdatephixslist || departure_ratio < 0) { const int upper = globals::allcont[i].upperlevel; - const double nnupperionlevel = get_levelpop(modelgridindex, element, ion + 1, upper); + const double nnupperionlevel = usecellhistupdatephixslist + ? get_levelpop(modelgridindex, element, ion + 1, upper) + : calculate_levelpop(modelgridindex, element, ion + 1, upper); const double sf = calculate_sahafact(element, ion, level, upper, T_e, H * nu_edge); departure_ratio = nnupperionlevel / nnlevel * nne * sf; // put that to phixslist - globals::cellhistory[tid].ch_allcont_departureratios[i] = departure_ratio; + if (usecellhistupdatephixslist) { + globals::cellhistory[tid].ch_allcont_departureratios[i] = departure_ratio; + } } const double stimfactor = departure_ratio * exp(-HOVERKB * nu / T_e); - corrfactor = 1 - stimfactor; // photoionisation minus stimulated recombination - if (corrfactor < 0) { - corrfactor = 0.; - } - // const double corrfactor = 1.; // no subtraction of stimulated recombination + corrfactor = std::max(0., 1 - stimfactor); // photoionisation minus stimulated recombination } - const double kappa_bf_contr = nnlevel * sigma_bf * probability * corrfactor; + const double sigma_contr = sigma_bf * probability * corrfactor; - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { + if constexpr (usecellhistupdatephixslist && (USE_LUT_PHOTOION || USE_LUT_BFHEATING)) { if (level == 0) { const int gphixsindex = globals::allcont[i].index_in_groundphixslist; - globals::phixslist[tid].groundcont_gamma_contr[gphixsindex] += sigma_bf * probability * corrfactor; + globals::phixslist[tid].groundcont_gamma_contr[gphixsindex] += sigma_contr; } } - if constexpr (DETAILED_BF_ESTIMATORS_ON) { - globals::phixslist[tid].gamma_contr[i] = sigma_bf * probability * corrfactor; + if constexpr (usecellhistupdatephixslist && DETAILED_BF_ESTIMATORS_ON) { + globals::phixslist[tid].gamma_contr[i] = sigma_contr; } - if (!std::isfinite(kappa_bf_contr)) { - printout("[fatal] calculate_kappa_rpkt_cont: non-finite contribution to kappa_bf_contr %g ... abort\n", - kappa_bf_contr); + const double chi_bf_contr = nnlevel * sigma_contr; + if (usecellhistupdatephixslist && !std::isfinite(chi_bf_contr)) { + printout("[fatal] calculate_chi_rpkt_cont: non-finite contribution to chi_bf_contr %g ... abort\n", + chi_bf_contr); printout("[fatal] phixslist index %d, element %d, ion %d, level %d\n", i, element, ion, level); printout("[fatal] Z=%d ionstage %d\n", get_atomicnumber(element), get_ionstage(element, ion)); printout("[fatal] globals::cell[%d].composition[%d].abundance = %g\n", modelgridindex, element, @@ -1315,53 +924,54 @@ auto calculate_kappa_bf_gammacontr(const int modelgridindex, const double nu) -> abort(); } - kappa_bf_sum += kappa_bf_contr; - globals::phixslist[tid].kappa_bf_sum[i] = kappa_bf_sum; - } else { + chi_bf_sum += chi_bf_contr; + if constexpr (usecellhistupdatephixslist) { + globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; + } + } else if constexpr (usecellhistupdatephixslist) { // ignore this particular process - globals::phixslist[tid].kappa_bf_sum[i] = kappa_bf_sum; + globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; if constexpr (DETAILED_BF_ESTIMATORS_ON) { globals::phixslist[tid].gamma_contr[i] = 0.; } } - } else // no element present or not an important level - { - globals::phixslist[tid].kappa_bf_sum[i] = kappa_bf_sum; + } else if constexpr (usecellhistupdatephixslist) { + // no element present or not an important level + globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; if constexpr (DETAILED_BF_ESTIMATORS_ON) { globals::phixslist[tid].gamma_contr[i] = 0.; } } } - for (; i < globals::nbfcontinua; i++) { - globals::phixslist[tid].kappa_bf_sum[i] = kappa_bf_sum; - if constexpr (DETAILED_BF_ESTIMATORS_ON) { - globals::phixslist[tid].gamma_contr[i] = 0.; + if constexpr (usecellhistupdatephixslist) { + for (; i < globals::nbfcontinua; i++) { + globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; + if constexpr (DETAILED_BF_ESTIMATORS_ON) { + globals::phixslist[tid].gamma_contr[i] = 0.; + } } } - return kappa_bf_sum; + + return chi_bf_sum; } -void calculate_kappa_rpkt_cont(const struct packet *const pkt_ptr, - struct rpkt_cont_opacity *kappa_rpkt_cont_thisthread) { - const int cellindex = pkt_ptr->where; - const int modelgridindex = grid::get_cell_modelgridindex(cellindex); - assert_always(modelgridindex != grid::get_npts_model()); - assert_always(grid::modelgrid[modelgridindex].thick != 1); - const double nu_cmf = pkt_ptr->nu_cmf; - if ((modelgridindex == kappa_rpkt_cont_thisthread->modelgridindex) && - (!kappa_rpkt_cont_thisthread->recalculate_required) && - (fabs(kappa_rpkt_cont_thisthread->nu / nu_cmf - 1.0) < 1e-4)) { +void calculate_chi_rpkt_cont(const double nu_cmf, struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont_thisthread, + const int modelgridindex, const bool usecellhistupdatephixslist) { + assert_testmodeonly(modelgridindex != grid::get_npts_model()); + assert_testmodeonly(grid::modelgrid[modelgridindex].thick != 1); + if ((modelgridindex == chi_rpkt_cont_thisthread->modelgridindex) && + (!chi_rpkt_cont_thisthread->recalculate_required) && (fabs(chi_rpkt_cont_thisthread->nu / nu_cmf - 1.0) < 1e-4)) { // calculated values are a match already return; } - const float nne = grid::get_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); double sigma = 0.0; - double kappa_ff = 0.; - double kappa_bf = 0.; - double kappa_ffheating = 0.; + double chi_ff = 0.; + double chi_bf = 0.; + double chi_ffheating = 0.; if (globals::opacity_case == 4) { /// First contribution: Thomson scattering on free electrons @@ -1373,55 +983,56 @@ void calculate_kappa_rpkt_cont(const struct packet *const pkt_ptr, // sigma *= 0.1; /// Second contribution: free-free absorption - kappa_ff = calculate_kappa_ff(modelgridindex, nu_cmf); - kappa_ffheating = kappa_ff; + chi_ff = calculate_chi_ff(modelgridindex, nu_cmf); + chi_ffheating = chi_ff; /// Third contribution: bound-free absorption - kappa_bf = calculate_kappa_bf_gammacontr(modelgridindex, nu_cmf); + chi_bf = usecellhistupdatephixslist ? calculate_chi_bf_gammacontr(modelgridindex, nu_cmf) + : calculate_chi_bf_gammacontr(modelgridindex, nu_cmf); // const double pkt_lambda = 1e8 * CLIGHT / nu_cmf; // if (pkt_lambda < 4000) // { - // printout("lambda %7.1f kappa_bf %g \n", pkt_lambda, kappa_bf); + // printout("lambda %7.1f chi_bf %g \n", pkt_lambda, chi_bf); // } } else { - /// in the other cases kappa_grey is an mass absorption coefficient + /// in the other cases chi_grey is an mass absorption coefficient /// therefore use the mass density - // sigma = globals::cell[pkt_ptr->where].kappa_grey * globals::cell[pkt_ptr->where].rho; + // sigma = globals::cell[pkt_ptr->where].chi_grey * globals::cell[pkt_ptr->where].rho; // sigma = SIGMA_T * nne; sigma = 0.; - // kappa_ff = 0.9*sigma; + // chi_ff = 0.9*sigma; // sigma *= 0.1; - // kappa_bf = 0.; + // chi_bf = 0.; // Second contribution: free-free absorption - kappa_ff = 1e5 * calculate_kappa_ff(modelgridindex, nu_cmf); + chi_ff = 1e5 * calculate_chi_ff(modelgridindex, nu_cmf); - kappa_bf = 0.; + chi_bf = 0.; } - kappa_rpkt_cont_thisthread->nu = nu_cmf; - kappa_rpkt_cont_thisthread->modelgridindex = modelgridindex; - kappa_rpkt_cont_thisthread->recalculate_required = false; - kappa_rpkt_cont_thisthread->total = sigma + kappa_bf + kappa_ff; - kappa_rpkt_cont_thisthread->es = sigma; - kappa_rpkt_cont_thisthread->ff = kappa_ff; - kappa_rpkt_cont_thisthread->bf = kappa_bf; - kappa_rpkt_cont_thisthread->ffheating = kappa_ffheating; - // kappa_rpkt_cont_thisthread.bfheating = kappa_bfheating; - - if (!std::isfinite(kappa_rpkt_cont_thisthread->total)) { - printout("[fatal] calculate_kappa_rpkt_cont: resulted in non-finite kappa_rpkt_cont.total ... abort\n"); - printout("[fatal] es %g, ff %g, bf %g\n", kappa_rpkt_cont_thisthread->es, kappa_rpkt_cont_thisthread->ff, - kappa_rpkt_cont_thisthread->bf); + chi_rpkt_cont_thisthread->nu = nu_cmf; + chi_rpkt_cont_thisthread->modelgridindex = modelgridindex; + chi_rpkt_cont_thisthread->recalculate_required = false; + chi_rpkt_cont_thisthread->total = sigma + chi_bf + chi_ff; + chi_rpkt_cont_thisthread->es = sigma; + chi_rpkt_cont_thisthread->ff = chi_ff; + chi_rpkt_cont_thisthread->bf = chi_bf; + chi_rpkt_cont_thisthread->ffheating = chi_ffheating; + // chi_rpkt_cont_thisthread.bfheating = chi_bfheating; + + if (!std::isfinite(chi_rpkt_cont_thisthread->total)) { + printout("[fatal] calculate_chi_rpkt_cont: resulted in non-finite chi_rpkt_cont.total ... abort\n"); + printout("[fatal] es %g, ff %g, bf %g\n", chi_rpkt_cont_thisthread->es, chi_rpkt_cont_thisthread->ff, + chi_rpkt_cont_thisthread->bf); printout("[fatal] nbfcontinua %d\n", globals::nbfcontinua); printout("[fatal] in cell %d with density %g\n", modelgridindex, grid::get_rho(modelgridindex)); - printout("[fatal] pkt_ptr->nu_cmf %g\n", pkt_ptr->nu_cmf); - if (std::isfinite(kappa_rpkt_cont_thisthread->es)) { - kappa_rpkt_cont_thisthread->ff = 0.; - kappa_rpkt_cont_thisthread->bf = 0.; - kappa_rpkt_cont_thisthread->total = kappa_rpkt_cont_thisthread->es; + printout("[fatal] pkt_ptr->nu_cmf %g\n", nu_cmf); + if (std::isfinite(chi_rpkt_cont_thisthread->es)) { + chi_rpkt_cont_thisthread->ff = 0.; + chi_rpkt_cont_thisthread->bf = 0.; + chi_rpkt_cont_thisthread->total = chi_rpkt_cont_thisthread->es; } else { abort(); } diff --git a/rpkt.h b/rpkt.h index 2c621dbad..6b4db7593 100644 --- a/rpkt.h +++ b/rpkt.h @@ -1,11 +1,33 @@ #ifndef RPKT_H #define RPKT_H +#include "artisoptions.h" +#include "constants.h" + void do_rpkt(struct packet *pkt_ptr, double t2); -void emitt_rpkt(struct packet *pkt_ptr); +void emit_rpkt(struct packet *pkt_ptr); int closest_transition(double nu_cmf, int next_trans); -double get_rpkt_escape_prob(struct packet *pkt_ptr, double tstart); -double calculate_kappa_bf_gammacontr(int modelgridindex, double nu); -void calculate_kappa_rpkt_cont(const struct packet *pkt_ptr, struct rpkt_cont_opacity *kappa_rpkt_cont_thisthread); +double calculate_chi_bf_gammacontr(int modelgridindex, double nu); +void calculate_chi_rpkt_cont(double nu_cmf, struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont_thisthread, + int modelgridindex, bool usecellhistupdatephixslist); + +constexpr auto get_linedistance(const double prop_time, const double nu_cmf, const double nu_trans, + const double d_nu_on_d_l) -> double { + // distance from packet position to redshifting into line at frequency nu_trans + + if (nu_cmf <= nu_trans) { + return 0; /// photon was propagated too far, make sure that we don't miss a line + } + + if constexpr (USE_RELATIVISTIC_DOPPLER_SHIFT) { + // With special relativity, the Doppler shift formula has an extra factor of 1/gamma in it, + // which changes the distance reach a line resonance and creates a dependence + // on packet position and direction + + // use linear interpolation of frequency along the path + return (nu_trans - nu_cmf) / d_nu_on_d_l; + } + return CLIGHT * prop_time * (nu_cmf / nu_trans - 1); +} #endif // RPKT_H diff --git a/scripts/artis-juwels.sh b/scripts/artis-juwels.sh index 296aba188..08d8b05c8 100755 --- a/scripts/artis-juwels.sh +++ b/scripts/artis-juwels.sh @@ -10,8 +10,9 @@ #SBATCH --mail-type=ALL ##SBATCH --mail-user=luke.shingles@gmail.com -module load Stages/2023 GCC ParaStationMPI -module load GSL +module load Stages/2024 GCC ParaStationMPI GSL + +module list cd $SLURM_SUBMIT_DIR diff --git a/scripts/artis-virgo-submit.sh b/scripts/artis-virgo-submit.sh index 887b76c8d..8d924a973 100755 --- a/scripts/artis-virgo-submit.sh +++ b/scripts/artis-virgo-submit.sh @@ -1,6 +1,6 @@ #!/bin/bash -x # 1920 cores and 4GB per core for 3D LTE kilonova models (and maybe 3D NLTE Type Ia?) -sbatch -J $(basename $(exec pwd)) --ntasks=1920 --mem-per-cpu=4096MB --partition=long --time=48:00:00 --constraint=intel --mail-type=ALL --mail-user=${USER}@gsi.de --no-requeue -- artis/scripts/artis-virgo-slurmjob.sh +sbatch -J $(basename $(exec pwd)) --ntasks=1920 --mem-per-cpu=4096MB --partition=long --time=48:00:00 --constraint=amd --mail-type=ALL --mail-user=${USER}@gsi.de --no-requeue -- artis/scripts/artis-virgo-slurmjob.sh # 960 cores and 1.5GB per core for simple 1D models (maybe 3D Type Ia without full NLTE?) #sbatch -J $(basename $(exec pwd)) --ntasks=960 --mem-per-cpu=1536M --partition=long --time=48:00:00 --constraint=amd --mail-type=ALL --mail-user=${USER}@gsi.de --no-requeue -- artis/scripts/artis-virgo-slurmjob.sh diff --git a/scripts/exspec-after.sh b/scripts/exspec-after.sh index 25080dde4..5c336b625 100755 --- a/scripts/exspec-after.sh +++ b/scripts/exspec-after.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# don't compress the files if we didn't successfully run exspec +# only compress the files if we successfully ran exspec if [ -f emission.out* ]; then if command -v zstd > /dev/null; then diff --git a/scripts/exspec-gzip-juwels.sh b/scripts/exspec-gzip-juwels.sh index aa8b3c070..070bd74c4 100755 --- a/scripts/exspec-gzip-juwels.sh +++ b/scripts/exspec-gzip-juwels.sh @@ -9,9 +9,7 @@ #SBATCH --mail-type=ALL ##SBATCH --mail-user=luke.shingles@gmail.com -module load Stages/2023 GCC ParaStationMPI GSL - -export PATH="/p/software/juwels/stages/2022/software/zstd/1.5.0-GCCcore-11.2.0/bin/:$PATH" +module load Stages/2023 GCC ParaStationMPI GSL zstd/.1.5.2 cd $SLURM_SUBMIT_DIR diff --git a/sn3d.cc b/sn3d.cc index ecdf03807..0125d0279 100644 --- a/sn3d.cc +++ b/sn3d.cc @@ -12,6 +12,8 @@ #include "sn3d.h" +#include + #include "atomic.h" #include "decay.h" #include "gammapkt.h" @@ -32,9 +34,7 @@ // threadprivate variables int tid; bool use_cellhist; -bool neutral_flag; -gsl_rng *rng = nullptr; -std::mt19937_64 *stdrng = nullptr; +std::mt19937 stdrng(std::random_device{}()); gsl_integration_workspace *gslworkspace = nullptr; FILE *output_file = nullptr; static FILE *linestat_file = nullptr; @@ -91,15 +91,15 @@ static void write_deposition_file(const int nts, const int my_rank, const int ns // for (int i = 0; i <= nts; i++) const int i = nts; { - const double t_mid = globals::time_step[i].mid; + const double t_mid = globals::timesteps[i].mid; // power in [erg/s] - globals::time_step[i].eps_positron_ana_power = 0.; - globals::time_step[i].eps_electron_ana_power = 0.; - globals::time_step[i].eps_alpha_ana_power = 0.; - globals::time_step[i].qdot_betaminus = 0.; - globals::time_step[i].qdot_alpha = 0.; - globals::time_step[i].qdot_total = 0.; + globals::timesteps[i].eps_positron_ana_power = 0.; + globals::timesteps[i].eps_electron_ana_power = 0.; + globals::timesteps[i].eps_alpha_ana_power = 0.; + globals::timesteps[i].qdot_betaminus = 0.; + globals::timesteps[i].qdot_alpha = 0.; + globals::timesteps[i].qdot_total = 0.; for (int mgi = nstart; mgi < (nstart + ndo); mgi++) // for (int mgi = 0; mgi < grid::get_npts_model(); mgi++) @@ -107,25 +107,25 @@ static void write_deposition_file(const int nts, const int my_rank, const int ns if (grid::get_numassociatedcells(mgi) > 0) { const double cellmass = grid::get_rho_tmin(mgi) * grid::get_modelcell_assocvolume_tmin(mgi); - globals::time_step[i].eps_positron_ana_power += + globals::timesteps[i].eps_positron_ana_power += cellmass * decay::get_particle_injection_rate(mgi, t_mid, decay::DECAYTYPE_BETAPLUS); - globals::time_step[i].eps_electron_ana_power += + globals::timesteps[i].eps_electron_ana_power += cellmass * decay::get_particle_injection_rate(mgi, t_mid, decay::DECAYTYPE_BETAMINUS); - globals::time_step[i].eps_alpha_ana_power += + globals::timesteps[i].eps_alpha_ana_power += cellmass * decay::get_particle_injection_rate(mgi, t_mid, decay::DECAYTYPE_ALPHA); if (i == nts) { mtot += cellmass; } - for (int dectypeindex = 0; dectypeindex < decay::DECAYTYPE_COUNT; dectypeindex++) { + for (const auto decaytype : decay::all_decaytypes) { // Qdot here has been multiplied by mass, so it is in units of [erg/s] - const double qdot_cell = decay::get_qdot_modelcell(mgi, t_mid, dectypeindex) * cellmass; - globals::time_step[i].qdot_total += qdot_cell; - if (dectypeindex == decay::DECAYTYPE_BETAMINUS) { - globals::time_step[i].qdot_betaminus += qdot_cell; - } else if (dectypeindex == decay::DECAYTYPE_ALPHA) { - globals::time_step[i].qdot_alpha += qdot_cell; + const double qdot_cell = decay::get_qdot_modelcell(mgi, t_mid, decaytype) * cellmass; + globals::timesteps[i].qdot_total += qdot_cell; + if (decaytype == decay::DECAYTYPE_BETAMINUS) { + globals::timesteps[i].qdot_betaminus += qdot_cell; + } else if (decaytype == decay::DECAYTYPE_ALPHA) { + globals::timesteps[i].qdot_alpha += qdot_cell; } } } @@ -133,12 +133,12 @@ static void write_deposition_file(const int nts, const int my_rank, const int ns #ifdef MPI_ON // in MPI mode, each process only did some fraction of the cells - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].eps_positron_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].eps_electron_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].eps_alpha_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].qdot_betaminus, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].qdot_alpha, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[i].qdot_total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].eps_positron_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].eps_electron_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].eps_alpha_ana_power, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].qdot_betaminus, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].qdot_alpha, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[i].qdot_total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); #endif } @@ -156,26 +156,26 @@ static void write_deposition_file(const int nts, const int my_rank, const int ns "Qdot_betaminus_ana_erg/s/g Qdotalpha_ana_erg/s/g eps_erg/s/g Qdot_ana_erg/s/g\n"); for (int i = 0; i <= nts; i++) { - const double t_mid = globals::time_step[i].mid; - const double t_width = globals::time_step[i].width; - const double total_dep = (globals::time_step[i].gamma_dep + globals::time_step[i].positron_dep + - globals::time_step[i].electron_dep + globals::time_step[i].alpha_dep); + const double t_mid = globals::timesteps[i].mid; + const double t_width = globals::timesteps[i].width; + const double total_dep = (globals::timesteps[i].gamma_dep + globals::timesteps[i].positron_dep + + globals::timesteps[i].electron_dep + globals::timesteps[i].alpha_dep); // dep is used here for positrons and alphas because it is the same as the emission rate - const double epsilon_mc = (globals::time_step[i].gamma_emission + globals::time_step[i].positron_dep + - globals::time_step[i].electron_emission + globals::time_step[i].alpha_emission) / + const double epsilon_mc = (globals::timesteps[i].gamma_emission + globals::timesteps[i].positron_dep + + globals::timesteps[i].electron_emission + globals::timesteps[i].alpha_emission) / mtot / t_width; fprintf(dep_file, "%d %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g\n", i, t_mid / DAY, t_mid, - total_dep / t_width / LSUN, globals::time_step[i].gamma_dep / t_width / LSUN, - globals::time_step[i].gamma_dep_pathint / t_width / LSUN, - globals::time_step[i].positron_dep / t_width / LSUN, globals::time_step[i].eps_positron_ana_power / LSUN, - globals::time_step[i].electron_dep / t_width / LSUN, - globals::time_step[i].electron_emission / t_width / LSUN, - globals::time_step[i].eps_electron_ana_power / LSUN, globals::time_step[i].alpha_dep / t_width / LSUN, - globals::time_step[i].alpha_emission / t_width / LSUN, globals::time_step[i].eps_alpha_ana_power / LSUN, - globals::time_step[i].gamma_emission / t_width / LSUN, globals::time_step[i].qdot_betaminus / mtot, - globals::time_step[i].qdot_alpha / mtot, epsilon_mc, globals::time_step[i].qdot_total / mtot); + total_dep / t_width / LSUN, globals::timesteps[i].gamma_dep / t_width / LSUN, + globals::timesteps[i].gamma_dep_pathint / t_width / LSUN, + globals::timesteps[i].positron_dep / t_width / LSUN, globals::timesteps[i].eps_positron_ana_power / LSUN, + globals::timesteps[i].electron_dep / t_width / LSUN, + globals::timesteps[i].electron_emission / t_width / LSUN, + globals::timesteps[i].eps_electron_ana_power / LSUN, globals::timesteps[i].alpha_dep / t_width / LSUN, + globals::timesteps[i].alpha_emission / t_width / LSUN, globals::timesteps[i].eps_alpha_ana_power / LSUN, + globals::timesteps[i].gamma_emission / t_width / LSUN, globals::timesteps[i].qdot_betaminus / mtot, + globals::timesteps[i].qdot_alpha / mtot, epsilon_mc, globals::timesteps[i].qdot_total / mtot); } fclose(dep_file); @@ -205,17 +205,17 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, if (grid::get_numassociatedcells(modelgridindex) > 0) { nonthermal::nt_MPI_Bcast(modelgridindex, root); - if (NLTE_POPS_ON && globals::rank_in_node == 0) { + if (globals::total_nlte_levels > 0 && globals::rank_in_node == 0) { MPI_Bcast(grid::modelgrid[modelgridindex].nlte_pops, globals::total_nlte_levels, MPI_DOUBLE, root_node_id, globals::mpi_comm_internode); } if constexpr (USE_LUT_PHOTOION) { assert_always(globals::corrphotoionrenorm != nullptr); - MPI_Bcast(&globals::corrphotoionrenorm[modelgridindex * get_nelements() * get_max_nions()], + MPI_Bcast(&globals::corrphotoionrenorm[get_ionestimindex(modelgridindex, 0, 0)], get_nelements() * get_max_nions(), MPI_DOUBLE, root, MPI_COMM_WORLD); assert_always(globals::gammaestimator != nullptr); - MPI_Bcast(&globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions()], + MPI_Bcast(&globals::gammaestimator[get_ionestimindex(modelgridindex, 0, 0)], get_nelements() * get_max_nions(), MPI_DOUBLE, root, MPI_COMM_WORLD); } } @@ -345,23 +345,23 @@ static void mpi_reduce_estimators(int nts) { MPI_Barrier(MPI_COMM_WORLD); /// Communicate gamma and positron deposition and write to file - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].cmf_lum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].gamma_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].positron_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].electron_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].electron_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].alpha_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].alpha_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &globals::time_step[nts].gamma_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - - globals::time_step[nts].cmf_lum /= globals::nprocs; - globals::time_step[nts].gamma_dep /= globals::nprocs; - globals::time_step[nts].positron_dep /= globals::nprocs; - globals::time_step[nts].electron_dep /= globals::nprocs; - globals::time_step[nts].electron_emission /= globals::nprocs; - globals::time_step[nts].alpha_dep /= globals::nprocs; - globals::time_step[nts].alpha_emission /= globals::nprocs; - globals::time_step[nts].gamma_emission /= globals::nprocs; + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].cmf_lum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].gamma_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].positron_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].electron_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].electron_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].alpha_dep, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].alpha_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &globals::timesteps[nts].gamma_emission, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + globals::timesteps[nts].cmf_lum /= globals::nprocs; + globals::timesteps[nts].gamma_dep /= globals::nprocs; + globals::timesteps[nts].positron_dep /= globals::nprocs; + globals::timesteps[nts].electron_dep /= globals::nprocs; + globals::timesteps[nts].electron_emission /= globals::nprocs; + globals::timesteps[nts].alpha_dep /= globals::nprocs; + globals::timesteps[nts].alpha_emission /= globals::nprocs; + globals::timesteps[nts].gamma_emission /= globals::nprocs; if constexpr (TRACK_ION_STATS) { stats::reduce_estimators(); @@ -466,25 +466,7 @@ static void save_grid_and_packets(const int nts, const int my_rank, struct packe // save packet state at start of current timestep (before propagation) write_temp_packetsfile(nts, my_rank, packets); - if constexpr (VPKT_ON) { - char filename[MAXFILENAMELENGTH]; - snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_%s.tmp", 0, my_rank, (nts % 2 == 0) ? "even" : "odd"); - - FILE *vspecpol_file = fopen_required(filename, "wb"); - - write_vspecpol(vspecpol_file); - fclose(vspecpol_file); - - // Write temporary files for vpkt_grid - if (vgrid_flag == 1) { - snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_%s.tmp", 0, my_rank, (nts % 2 == 0) ? "even" : "odd"); - - FILE *vpkt_grid_file = fopen_required(filename, "wb"); - - write_vpkt_grid(vpkt_grid_file); - fclose(vpkt_grid_file); - } - } + vpkt_write_timestep(nts, my_rank, tid, false); const time_t time_write_packets_file_finished = time(nullptr); @@ -531,6 +513,7 @@ static void save_grid_and_packets(const int nts, const int my_rank, struct packe // delete temp packets files from previous timestep now that all restart data for the new timestep is available remove_temp_packetsfile(nts - 1, my_rank); + vpkt_remove_temp_file(nts - 1, my_rank); } } @@ -550,10 +533,10 @@ static void zero_estimators() { for (int element = 0; element < get_nelements(); element++) { for (int ion = 0; ion < get_max_nions(); ion++) { if constexpr (USE_LUT_PHOTOION) { - globals::gammaestimator[n * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = 0.; + globals::gammaestimator[get_ionestimindex(n, element, ion)] = 0.; } if constexpr (USE_LUT_BFHEATING) { - globals::bfheatingestimator[n * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = 0.; + globals::bfheatingestimator[get_ionestimindex(n, element, ion)] = 0.; } } } @@ -568,7 +551,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const bool do_this_full_loop = true; const int nts_prev = (titer != 0 || nts == 0) ? nts : nts - 1; - if ((titer > 0) || (globals::simulation_continued_from_saved && (nts == globals::itstep))) { + if ((titer > 0) || (globals::simulation_continued_from_saved && (nts == globals::timestep_initial))) { /// Read the packets file to reset before each additional iteration on the timestep read_temp_packetsfile(nts, my_rank, packets); } @@ -605,7 +588,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const /// If this is not the 0th time step of the current job step, /// write out a snapshot of the grid properties for further restarts /// and update input.txt accordingly - if (((nts - globals::itstep) != 0)) { + if (((nts - globals::timestep_initial) != 0)) { save_grid_and_packets(nts, my_rank, packets); do_this_full_loop = walltime_sufficient_to_continue(nts, nts_prev, walltimelimitseconds); } @@ -617,7 +600,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const zero_estimators(); // MPI_Barrier(MPI_COMM_WORLD); - if ((nts < globals::ftstep) && do_this_full_loop) { + if ((nts < globals::timestep_finish) && do_this_full_loop) { /// Now process the packets. update_packets(my_rank, nts, packets); @@ -646,9 +629,9 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const #endif printout("During timestep %d on MPI process %d, %d pellets decayed and %d packets escaped. (t=%gd)\n", nts, my_rank, - globals::time_step[nts].pellet_decays, globals::nesc, globals::time_step[nts].mid / DAY); + globals::timesteps[nts].pellet_decays.load(), globals::nesc.load(), globals::timesteps[nts].mid / DAY); - if constexpr (VPKT_ON) { + if (VPKT_ON) { printout("During timestep %d on MPI process %d, %d virtual packets were generated and %d escaped. \n", nts, my_rank, nvpkt, nvpkt_esc1 + nvpkt_esc2 + nvpkt_esc3); printout( @@ -679,27 +662,13 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const } } - if (nts == globals::ftstep - 1) { + if (nts == globals::timestep_finish - 1) { char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "packets%.2d_%.4d.out", 0, my_rank); // snprintf(filename, MAXFILENAMELENGTH, "packets%.2d_%.4d.out", middle_iteration, my_rank); write_packets(filename, packets); - // write specpol of the virtual packets - if constexpr (VPKT_ON) { - snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d-%d.out", my_rank, tid); - FILE *vspecpol_file = fopen_required(filename, "w"); - - write_vspecpol(vspecpol_file); - fclose(vspecpol_file); - - if (vgrid_flag == 1) { - snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d-%d.out", my_rank, tid); - FILE *vpkt_grid_file = fopen_required(filename, "w"); - write_vpkt_grid(vpkt_grid_file); - fclose(vpkt_grid_file); - } - } + vpkt_write_timestep(nts, my_rank, tid, true); printout("time after write final packets file %ld\n", time(nullptr)); @@ -730,42 +699,12 @@ auto main(int argc, char *argv[]) -> int { #ifdef MPI_ON MPI_Init(&argc, &argv); - MPI_Comm_rank(MPI_COMM_WORLD, &globals::rank_global); - MPI_Comm_size(MPI_COMM_WORLD, &globals::nprocs); - - // make an intra-node communicator (group ranks that can share memory) - MPI_Comm_split_type(MPI_COMM_WORLD, MPI_COMM_TYPE_SHARED, globals::rank_global, MPI_INFO_NULL, - &globals::mpi_comm_node); - // get the local rank within this node - MPI_Comm_rank(globals::mpi_comm_node, &globals::rank_in_node); - // get the number of ranks on the node - MPI_Comm_size(globals::mpi_comm_node, &globals::node_nprocs); - MPI_Barrier(MPI_COMM_WORLD); - - // make an inter-node communicator (using local rank as the key for group membership) - MPI_Comm_split(MPI_COMM_WORLD, globals::rank_in_node, globals::rank_global, &globals::mpi_comm_internode); - - // take the node id from the local rank 0 (node master) and broadcast it - if (globals::rank_in_node == 0) { - MPI_Comm_rank(globals::mpi_comm_internode, &globals::node_id); - MPI_Comm_size(globals::mpi_comm_internode, &globals::node_count); - } - - MPI_Bcast(&globals::node_id, 1, MPI_INT, 0, globals::mpi_comm_node); - MPI_Bcast(&globals::node_count, 1, MPI_INT, 0, globals::mpi_comm_node); - -#else - globals::rank_global = 0; - globals::nprocs = 1; - globals::rank_in_node = 0; - globals::node_nprocs = 1; - globals::node_id = 0; - globals::node_count = 0; #endif - const int my_rank = globals::rank_global; + globals::setup_mpi_vars(); - if (my_rank == 0) { + globals::startofline = std::make_unique(get_max_threads()); + if (globals::rank_global == 0) { check_already_running(); } @@ -774,7 +713,7 @@ auto main(int argc, char *argv[]) -> int { MPI_Barrier(MPI_COMM_WORLD); #endif - globals::startofline = std::make_unique(get_max_threads()); + const int my_rank = globals::rank_global; #ifdef _OPENMP /// Explicitly turn off dynamic threads because we use the threadprivate directive!!! @@ -795,7 +734,7 @@ auto main(int argc, char *argv[]) -> int { #ifdef _OPENMP printout("OpenMP parallelisation active with %d threads (max %d)\n", get_num_threads(), get_max_threads()); #else - printout("OpenMP is not available in this build\n"); + printout("OpenMP is not enabled in this build (this is normal)\n"); #endif gslworkspace = gsl_integration_workspace_alloc(GSLWSIZE); @@ -818,7 +757,7 @@ auto main(int argc, char *argv[]) -> int { printout("walltimelimitseconds = %d\n", walltimelimitseconds); } else { fprintf(stderr, "Usage: %s [-w WALLTIMELIMITHOURS]\n", argv[0]); - exit(EXIT_FAILURE); + abort(); } } @@ -850,18 +789,23 @@ auto main(int argc, char *argv[]) -> int { printout("MPI is disabled in this build\n"); #endif - globals::kappa_rpkt_cont = - static_cast(calloc(get_max_threads(), sizeof(struct rpkt_cont_opacity))); - assert_always(globals::kappa_rpkt_cont != nullptr); + globals::chi_rpkt_cont = static_cast( + calloc(get_max_threads(), sizeof(struct rpkt_continuum_absorptioncoeffs))); + assert_always(globals::chi_rpkt_cont != nullptr); input(my_rank); + if (globals::simulation_continued_from_saved) { + assert_always(globals::nprocs_exspec == globals::nprocs); + } else { + globals::nprocs_exspec = globals::nprocs; + } if (my_rank == 0) { initialise_linestat_file(); } printout("time after input %ld\n", time(nullptr)); - printout("timesteps %d\n", globals::ntstep); + printout("timesteps %d\n", globals::ntimesteps); /// Precalculate the rate coefficients for spontaneous and stimulated recombination /// and for photoionisation. With the nebular approximation they only depend on T_e @@ -916,9 +860,9 @@ auto main(int argc, char *argv[]) -> int { /// The next loop is over all grid cells. For parallelisation, we want to split this loop between /// processes. This is done by assigning each MPI process nblock cells. The residual n_leftover /// cells are sent to processes 0 ... process n_leftover -1. - int const nstart = grid::get_nstart(my_rank); - int const ndo = grid::get_ndo(my_rank); - int const ndo_nonempty = grid::get_ndo_nonempty(my_rank); + const int nstart = grid::get_nstart(my_rank); + const int ndo = grid::get_ndo(my_rank); + const int ndo_nonempty = grid::get_ndo_nonempty(my_rank); printout("process rank %d (global max rank %d) assigned %d modelgrid cells (%d nonempty)", my_rank, globals::nprocs - 1, ndo, ndo_nonempty); if (ndo > 0) { @@ -929,7 +873,7 @@ auto main(int argc, char *argv[]) -> int { #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); - int const maxndo = grid::get_maxndo(); + const int maxndo = grid::get_maxndo(); /// Initialise the exchange buffer /// The factor 4 comes from the fact that our buffer should contain elements of 4 byte /// instead of 1 byte chars. But the MPI routines don't care about the buffers datatype @@ -940,7 +884,7 @@ auto main(int argc, char *argv[]) -> int { MPI_Barrier(MPI_COMM_WORLD); #endif - int nts = globals::itstep; + int nts = globals::timestep_initial; macroatom_open_file(my_rank); if (ndo > 0) { @@ -948,41 +892,16 @@ auto main(int argc, char *argv[]) -> int { snprintf(filename, MAXFILENAMELENGTH, "estimators_%.4d.out", my_rank); estimators_file = fopen_required(filename, "w"); - if (NLTE_POPS_ON && ndo_nonempty > 0) { + if (globals::total_nlte_levels > 0 && ndo_nonempty > 0) { nltepop_open_file(my_rank); } } - // Initialise virtual packets file and vspecpol - if constexpr (VPKT_ON) { - init_vspecpol(); - if (vgrid_flag == 1) { - init_vpkt_grid(); - } - - if (globals::simulation_continued_from_saved) { - // Continue simulation: read into temporary files - - read_vspecpol(my_rank, nts); - - if (vgrid_flag == 1) { - if (nts % 2 == 0) { - snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_odd.tmp", 0, my_rank); - } else { - snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_even.tmp", 0, my_rank); - } + // initialise or read in virtual packet spectra + vpkt_init(nts, my_rank, tid, globals::simulation_continued_from_saved); - FILE *vpktgrid_file = fopen_required(filename, "rb"); - - read_vpkt_grid(vpktgrid_file); - - fclose(vpktgrid_file); - } - } - } - - while (nts < globals::ftstep && !terminate_early) { - globals::nts_global = nts; + while (nts < globals::timestep_finish && !terminate_early) { + globals::timestep = nts; #ifdef MPI_ON // const time_t time_before_barrier = time(nullptr); MPI_Barrier(MPI_COMM_WORLD); @@ -991,16 +910,12 @@ auto main(int argc, char *argv[]) -> int { // time_after_barrier); #endif -#ifdef DO_TITER - // The first time step must solve the ionisation balance in LTE - globals::initial_iteration = (nts == 0); -#else /// titer example: Do 3 iterations on timestep 0-6 // globals::n_titer = (nts < 6) ? 3: 1; globals::n_titer = 1; - globals::initial_iteration = (nts < globals::num_lte_timesteps); -#endif + globals::lte_iteration = (nts < globals::num_lte_timesteps); + assert_always(globals::num_lte_timesteps > 0); // The first time step must solve the ionisation balance in LTE for (int titer = 0; titer < globals::n_titer; titer++) { terminate_early = do_timestep(nts, titer, my_rank, nstart, ndo, packets, walltimelimitseconds); @@ -1027,7 +942,7 @@ auto main(int argc, char *argv[]) -> int { fclose(linestat_file); } - if ((globals::ntstep != globals::ftstep) || (terminate_early)) { + if ((globals::ntimesteps != globals::timestep_finish) || (terminate_early)) { printout("RESTART_NEEDED to continue model\n"); } else { printout("No need for restart\n"); @@ -1038,7 +953,7 @@ auto main(int argc, char *argv[]) -> int { #endif const time_t real_time_end = time(nullptr); - printout("simulation finished at %ld (this job wallclock hours %.2f * %d CPUs = %.1f CPU hours)\n", real_time_end, + printout("sn3d finished at %ld (this job wallclock hours %.2f * %d CPUs = %.1f CPU hours)\n", real_time_end, (real_time_end - real_time_start) / 3600., globals::nprocs, (real_time_end - real_time_start) / 3600. * globals::nprocs); @@ -1047,9 +962,7 @@ auto main(int argc, char *argv[]) -> int { } macroatom_close_file(); - if (NLTE_POPS_ON) { - nltepop_close_file(); - } + nltepop_close_file(); radfield::close_file(); nonthermal::close_file(); @@ -1076,8 +989,9 @@ auto main(int argc, char *argv[]) -> int { MPI_Finalize(); #endif - if (std::filesystem::exists("artis.pid")) { - std::filesystem::remove("artis.pid"); + const std::filesystem::path pid_file_path("artis.pid"); + if (std::filesystem::exists(pid_file_path)) { + std::filesystem::remove(pid_file_path); } return 0; diff --git a/sn3d.h b/sn3d.h index f2b7a8998..09067bba3 100644 --- a/sn3d.h +++ b/sn3d.h @@ -7,12 +7,9 @@ #include #include -#include #include -#include #include #include -#include #include #include #include @@ -33,17 +30,13 @@ extern FILE *output_file; extern int tid; extern bool use_cellhist; -extern bool neutral_flag; -#include -extern gsl_rng *rng; // pointer for random number generator -extern std::mt19937_64 *stdrng; -static std::uniform_real_distribution stdrngdis(0.0, 1.0); +extern std::mt19937 stdrng; extern gsl_integration_workspace *gslworkspace; #ifdef _OPENMP -#pragma omp threadprivate(tid, use_cellhist, neutral_flag, rng, gslworkspace, output_file) +#pragma omp threadprivate(tid, use_cellhist, stdrng, gslworkspace, output_file) #endif #define __artis_assert(e) \ @@ -115,7 +108,7 @@ static inline int get_bflutindex(const int tempindex, const int element, const i #ifdef _OPENMP #define safeadd(var, val) _Pragma("omp atomic update") var += val #else -#define safeadd(var, val) var += val +#define safeadd(var, val) var = var + val #endif #define safeincrement(var) safeadd(var, 1) @@ -130,28 +123,41 @@ static inline void gsl_error_handler_printout(const char *reason, const char *fi } } -static FILE *fopen_required(const char *filename, const char *mode) { - assert_always(filename != nullptr); - std::string datafolderfilename("data/"); - datafolderfilename += filename; +static FILE *fopen_required(const std::string &filename, const char *mode) { + // look in the data folder first + const std::string datafolderfilename = "data/" + filename; if (mode[0] == 'r' && std::filesystem::exists(datafolderfilename)) { - return fopen_required(datafolderfilename.c_str(), mode); + return fopen_required(datafolderfilename, mode); } - FILE *file = std::fopen(filename, mode); + + FILE *file = std::fopen(filename.c_str(), mode); if (file == nullptr) { - printout("ERROR: Could not open file '%s' for mode '%s'.\n", filename, mode); + printout("ERROR: Could not open file '%s' for mode '%s'.\n", filename.c_str(), mode); abort(); } return file; } +static std::fstream fstream_required(const std::string &filename, std::ios_base::openmode mode) { + const std::string datafolderfilename = "data/" + filename; + if (mode == std::ios::in && std::filesystem::exists(datafolderfilename)) { + return fstream_required(datafolderfilename, mode); + } + auto file = std::fstream(filename, mode); + if (!file.is_open()) { + printout("ERROR: Could not open file '%s'\n", filename.c_str()); + abort(); + } + return file; +} + static int get_timestep(const double time) { assert_always(time >= globals::tmin); assert_always(time < globals::tmax); - for (int nts = 0; nts < globals::ntstep; nts++) { - const double tsend = (nts < (globals::ntstep - 1)) ? globals::time_step[nts + 1].start : globals::tmax; - if (time >= globals::time_step[nts].start && time < tsend) { + for (int nts = 0; nts < globals::ntimesteps; nts++) { + const double tsend = (nts < (globals::ntimesteps - 1)) ? globals::timesteps[nts + 1].start : globals::tmax; + if (time >= globals::timesteps[nts].start && time < tsend) { return nts; } } @@ -184,35 +190,25 @@ inline int get_thread_num(void) { #endif } -inline double rng_uniform(void) { - if constexpr (USE_GSL_RANDOM) { - return gsl_rng_uniform(rng); - } else { - return stdrngdis(*stdrng); - } +inline float rng_uniform(void) { + float zrand; + do { + zrand = std::generate_canonical::digits>(stdrng); + } while (zrand == 1.); + return zrand; } -inline double rng_uniform_pos(void) { - if constexpr (USE_GSL_RANDOM) { - return gsl_rng_uniform_pos(rng); - } else { - double zrand = 0.; - do { - zrand = rng_uniform(); - } while (zrand <= 0.); - return zrand; - } +inline float rng_uniform_pos(void) { + float zrand = 0.; + do { + zrand = rng_uniform(); + } while (zrand <= 0.); + return zrand; } inline void rng_init(const uint_fast64_t zseed) { - if constexpr (USE_GSL_RANDOM) { - rng = gsl_rng_alloc(gsl_rng_ran3); - gsl_rng_set(rng, zseed); - printout("rng is a '%s' generator\n", gsl_rng_name(rng)); - } else { - printout("rng is a std::mt19937_64 generator\n"); - stdrng = new std::mt19937_64(zseed); - } + printout("rng is a std::mt19937 generator\n"); + stdrng.seed(zseed); } inline bool is_pid_running(pid_t pid) { @@ -229,7 +225,7 @@ inline void check_already_running(void) { pid_t artispid = getpid(); if (std::filesystem::exists("artis.pid")) { - std::ifstream pidfile("artis.pid", std::ifstream::in); + auto pidfile = std::fstream("artis.pid", std::ios::in); pid_t artispid_in; pidfile >> artispid_in; pidfile.close(); @@ -243,9 +239,13 @@ inline void check_already_running(void) { } } - std::ofstream pidfile("artis.pid", std::ofstream::out | std::ofstream::trunc); + auto pidfile = std::fstream("artis.pid", std::ofstream::out | std::ofstream::trunc); pidfile << artispid; pidfile.close(); } +inline int get_ionestimindex(const int mgi, const int element, const int ion) { + return mgi * get_nelements() * get_max_nions() + element * get_max_nions() + ion; +} + #endif // SN3D_H diff --git a/spectrum.cc b/spectrum.cc index c5c1af5f8..1417121f9 100644 --- a/spectrum.cc +++ b/spectrum.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include "atomic.h" #include "exspec.h" @@ -28,37 +29,11 @@ using emissionabsorptioncontrib = struct emissionabsorptioncontrib { int lineindex; // this will be important when the list gets sorted }; -static struct emissionabsorptioncontrib *traceemissionabsorption = nullptr; +static std::vector traceemissionabsorption; double traceemission_totalenergy = 0.; double traceabsorption_totalenergy = 0.; -std::unique_ptr rpkt_spectra = nullptr; - -static auto compare_emission(const void *p1, const void *p2) -> int { - const auto *elem1 = static_cast(p1); - const auto *elem2 = static_cast(p2); - - if (elem1->energyemitted < elem2->energyemitted) { - return 1; - } - if (elem1->energyemitted > elem2->energyemitted) { - return -1; - } - return 0; -} - -static auto compare_absorption(const void *p1, const void *p2) -> int { - const auto *elem1 = static_cast(p1); - const auto *elem2 = static_cast(p2); - - if (elem1->energyabsorbed < elem2->energyabsorbed) { - return 1; - } - if (elem1->energyabsorbed > elem2->energyabsorbed) { - return -1; - } - return 0; -} +struct spec rpkt_spectra; static void printout_tracemission_stats() { const int maxlinesprinted = 500; @@ -66,7 +41,8 @@ static void printout_tracemission_stats() { // mode is 0 for emission and 1 for absorption for (int mode = 0; mode < 2; mode++) { if (mode == 0) { - qsort(traceemissionabsorption, globals::nlines, sizeof(emissionabsorptioncontrib), compare_emission); + std::sort(traceemissionabsorption.begin(), traceemissionabsorption.end(), + [](const auto &a, const auto &b) { return a.energyemitted > b.energyemitted; }); printout("lambda [%5.1f, %5.1f] nu %g %g\n", traceemissabs_lambdamin, traceemissabs_lambdamax, traceemissabs_nulower, traceemissabs_nuupper); @@ -74,7 +50,8 @@ static void printout_tracemission_stats() { traceemissabs_lambdamin, traceemissabs_lambdamax, traceemissabs_timemin / DAY, traceemissabs_timemax / DAY, traceemission_totalenergy); } else { - qsort(traceemissionabsorption, globals::nlines, sizeof(emissionabsorptioncontrib), compare_absorption); + std::sort(traceemissionabsorption.begin(), traceemissionabsorption.end(), + [](const auto &a, const auto &b) { return a.energyabsorbed > b.energyabsorbed; }); printout("Top line absorption contributions in the range lambda [%5.1f, %5.1f] time [%5.1fd, %5.1fd] (%g erg)\n", traceemissabs_lambdamin, traceemissabs_lambdamax, traceemissabs_timemin / DAY, traceemissabs_timemax / DAY, traceabsorption_totalenergy); @@ -127,7 +104,7 @@ static void printout_tracemission_stats() { const int nupperdowntrans = get_ndowntrans(element, ion, upper); auto *downtranslist = globals::elements[element].ions[ion].levels[upper].downtrans; auto *downtrans = std::find_if(downtranslist, downtranslist + nupperdowntrans, - [=](auto &downtrans) { return downtrans.targetlevelindex == lower; }); + [=](const auto &downtrans) { return downtrans.targetlevelindex == lower; }); assert_always(downtrans != (downtranslist + nupperdowntrans)); printout("%7.2e (%5.1f%%) %4d %9d %5d %5d %8.1f %8.2e %4d %7.1f %7.1f %7.1e %7.1e\n", encontrib, @@ -142,8 +119,7 @@ static void printout_tracemission_stats() { printout("\n"); } - free(traceemissionabsorption); - traceemissionabsorption = nullptr; + traceemissionabsorption.clear(); } static auto get_proccount() -> int @@ -152,15 +128,16 @@ static auto get_proccount() -> int return 2 * get_nelements() * get_max_nions() + 1; } -void write_spectrum(const std::string &spec_filename, const char *emission_filename, const char *trueemission_filename, - const char *absorption_filename, const struct spec &spectra, int numtimesteps) { - FILE *spec_file = fopen_required(spec_filename.c_str(), "w"); +void write_spectrum(const std::string &spec_filename, const std::string &emission_filename, + const std::string &trueemission_filename, const std::string &absorption_filename, + const struct spec &spectra, int numtimesteps) { + FILE *spec_file = fopen_required(spec_filename, "w"); FILE *emission_file = nullptr; FILE *trueemission_file = nullptr; FILE *absorption_file = nullptr; - bool const do_emission_res = spectra.do_emission_res; + const bool do_emission_res = spectra.do_emission_res; if (do_emission_res) { emission_file = fopen_required(emission_filename, "w"); @@ -169,21 +146,21 @@ void write_spectrum(const std::string &spec_filename, const char *emission_filen assert_always(trueemission_file != nullptr); absorption_file = fopen_required(absorption_filename, "w"); assert_always(absorption_file != nullptr); - printout("Writing %s, %s, %s, and %s\n", spec_filename.c_str(), emission_filename, trueemission_filename, - absorption_filename); + printout("Writing %s, %s, %s, and %s\n", spec_filename.c_str(), emission_filename.c_str(), + trueemission_filename.c_str(), absorption_filename.c_str()); } else { printout("Writing %s\n", spec_filename.c_str()); } - if (TRACE_EMISSION_ABSORPTION_REGION_ON && do_emission_res && traceemissionabsorption != nullptr) { + if (TRACE_EMISSION_ABSORPTION_REGION_ON && do_emission_res && !traceemissionabsorption.empty()) { printout_tracemission_stats(); } - assert_always(numtimesteps <= globals::ntstep); + assert_always(numtimesteps <= globals::ntimesteps); fprintf(spec_file, "%g ", 0.0); for (int p = 0; p < numtimesteps; p++) { - fprintf(spec_file, "%g ", globals::time_step[p].mid / DAY); + fprintf(spec_file, "%g ", globals::timesteps[p].mid / DAY); } fprintf(spec_file, "\n"); @@ -223,17 +200,17 @@ void write_spectrum(const std::string &spec_filename, const char *emission_filen } void write_specpol(const std::string &specpol_filename, const std::string &emission_filename, - const std::string &absorption_filename, struct spec *stokes_i, struct spec *stokes_q, - struct spec *stokes_u) { - FILE *specpol_file = fopen_required(specpol_filename.c_str(), "w"); + const std::string &absorption_filename, const struct spec *stokes_i, const struct spec *stokes_q, + const struct spec *stokes_u) { + FILE *specpol_file = fopen_required(specpol_filename, "w"); FILE *emissionpol_file = nullptr; FILE *absorptionpol_file = nullptr; const bool do_emission_res = stokes_i->do_emission_res; if (do_emission_res) { - emissionpol_file = fopen_required(emission_filename.c_str(), "w"); - absorptionpol_file = fopen_required(absorption_filename.c_str(), "w"); + emissionpol_file = fopen_required(emission_filename, "w"); + absorptionpol_file = fopen_required(absorption_filename, "w"); printout("Writing %s, %s, and %s\n", specpol_filename.c_str(), emission_filename.c_str(), absorption_filename.c_str()); } else { @@ -243,8 +220,8 @@ void write_specpol(const std::string &specpol_filename, const std::string &emiss fprintf(specpol_file, "%g ", 0.0); for (int l = 0; l < 3; l++) { - for (int p = 0; p < globals::ntstep; p++) { - fprintf(specpol_file, "%g ", globals::time_step[p].mid / DAY); + for (int p = 0; p < globals::ntimesteps; p++) { + fprintf(specpol_file, "%g ", globals::timesteps[p].mid / DAY); } } @@ -252,11 +229,12 @@ void write_specpol(const std::string &specpol_filename, const std::string &emiss const int proccount = get_proccount(); const int ioncount = get_nelements() * get_max_nions(); - for (int m = 0; m < MNUBINS; m++) { + assert_always(stokes_i->lower_freq.size() == stokes_i->delta_freq.size()); + for (size_t m = 0; m < stokes_i->lower_freq.size(); m++) { fprintf(specpol_file, "%g ", ((stokes_i->lower_freq[m] + (stokes_i->delta_freq[m] / 2)))); // Stokes I - for (int p = 0; p < globals::ntstep; p++) { + for (int p = 0; p < globals::ntimesteps; p++) { fprintf(specpol_file, "%g ", stokes_i->timesteps[p].flux[m]); if (do_emission_res) { @@ -273,7 +251,7 @@ void write_specpol(const std::string &specpol_filename, const std::string &emiss } // Stokes Q - for (int p = 0; p < globals::ntstep; p++) { + for (int p = 0; p < globals::ntimesteps; p++) { fprintf(specpol_file, "%g ", stokes_q->timesteps[p].flux[m]); if (do_emission_res) { @@ -290,7 +268,7 @@ void write_specpol(const std::string &specpol_filename, const std::string &emiss } // Stokes U - for (int p = 0; p < globals::ntstep; p++) { + for (int p = 0; p < globals::ntimesteps; p++) { fprintf(specpol_file, "%g ", stokes_u->timesteps[p].flux[m]); if (do_emission_res) { @@ -352,7 +330,7 @@ static auto columnindex_from_emissiontype(const int et) -> int { } static void add_to_spec(const struct packet *const pkt_ptr, const int current_abin, struct spec &spectra, - struct spec *stokes_i, struct spec *stokes_q, struct spec *stokes_u) + const struct spec *stokes_i, const struct spec *stokes_q, const struct spec *stokes_u) // Routine to add a packet to the outgoing spectrum. { // Need to (1) decide which time bin to put it in and (2) which frequency bin. @@ -370,8 +348,8 @@ static void add_to_spec(const struct packet *const pkt_ptr, const int current_ab const int nnu = static_cast((log(pkt_ptr->nu_rf) - log(nu_min)) / dlognu); assert_always(nnu < MNUBINS); - const double deltaE = pkt_ptr->e_rf / globals::time_step[nt].width / spectra.delta_freq[nnu] / 4.e12 / PI / PARSEC / - PARSEC / globals::nprocs * anglefactor; + const double deltaE = pkt_ptr->e_rf / globals::timesteps[nt].width / spectra.delta_freq[nnu] / 4.e12 / PI / PARSEC / + PARSEC / globals::nprocs_exspec * anglefactor; spectra.timesteps[nt].flux[nnu] += deltaE; @@ -428,8 +406,8 @@ static void add_to_spec(const struct packet *const pkt_ptr, const int current_ab const int nnu_abs = static_cast((log(pkt_ptr->absorptionfreq) - log(nu_min)) / dlognu); if (nnu_abs >= 0 && nnu_abs < MNUBINS) { const int ioncount = get_nelements() * get_max_nions(); - const double deltaE_absorption = pkt_ptr->e_rf / globals::time_step[nt].width / spectra.delta_freq[nnu_abs] / - 4.e12 / PI / PARSEC / PARSEC / globals::nprocs * anglefactor; + const double deltaE_absorption = pkt_ptr->e_rf / globals::timesteps[nt].width / spectra.delta_freq[nnu_abs] / + 4.e12 / PI / PARSEC / PARSEC / globals::nprocs_exspec * anglefactor; const int at = pkt_ptr->absorptiontype; if (at >= 0) { /// bb-emission @@ -472,8 +450,7 @@ static void add_to_spec(const struct packet *const pkt_ptr, const int current_ab void init_spectrum_trace() { if (TRACE_EMISSION_ABSORPTION_REGION_ON) { traceemission_totalenergy = 0.; - traceemissionabsorption = - static_cast(malloc(globals::nlines * sizeof(emissionabsorptioncontrib))); + traceemissionabsorption.resize(globals::nlines); traceabsorption_totalenergy = 0.; for (int i = 0; i < globals::nlines; i++) { traceemissionabsorption[i].energyemitted = 0.; @@ -491,120 +468,80 @@ void init_spectra(struct spec &spectra, const double nu_min, const double nu_max // step sizes first. assert_always(MNUBINS > 0); - + size_t mem_usage = 0; const double dlognu = (log(nu_max) - log(nu_min)) / MNUBINS; spectra.nu_min = nu_min; spectra.nu_max = nu_max; spectra.do_emission_res = do_emission_res; - assert_always(spectra.lower_freq.get() != nullptr); - assert_always(spectra.delta_freq.get() != nullptr); - for (int nnu = 0; nnu < MNUBINS; nnu++) { + const bool print_memusage = + (spectra.lower_freq.empty() || (do_emission_res && spectra.absorptionalltimesteps.empty())); + + spectra.lower_freq.resize(MNUBINS); + spectra.delta_freq.resize(spectra.lower_freq.size()); + for (size_t nnu = 0; nnu < spectra.lower_freq.size(); nnu++) { spectra.lower_freq[nnu] = exp(log(nu_min) + (nnu * (dlognu))); spectra.delta_freq[nnu] = exp(log(nu_min) + ((nnu + 1) * (dlognu))) - spectra.lower_freq[nnu]; } - assert_always(spectra.timesteps != nullptr); + spectra.do_emission_res = do_emission_res; // might be set true later by alloc_emissionabsorption_spectra - const int proccount = get_proccount(); - const int ioncount = get_nelements() * get_max_nions(); - for (int nts = 0; nts < globals::ntstep; nts++) { - assert_always(spectra.timesteps[nts].flux != nullptr); - for (int nnu = 0; nnu < MNUBINS; nnu++) { - spectra.timesteps[nts].flux[nnu] = 0.0; - } + spectra.timesteps.resize(globals::ntimesteps); + spectra.fluxalltimesteps.resize(globals::ntimesteps * MNUBINS); + std::ranges::fill(spectra.fluxalltimesteps, 0.0); - if (do_emission_res) { - assert_always(spectra.timesteps[nts].emission != nullptr); - assert_always(spectra.timesteps[nts].trueemission != nullptr); - for (int i = 0; i < MNUBINS * proccount; i++) { - spectra.timesteps[nts].emission[i] = 0; - spectra.timesteps[nts].trueemission[i] = 0; - } + mem_usage += globals::ntimesteps * sizeof(struct spec); + mem_usage += globals::ntimesteps * sizeof(struct timestepspec); + mem_usage += globals::ntimesteps * MNUBINS * sizeof(double); - assert_always(spectra.timesteps[nts].absorption != nullptr); - for (int i = 0; i < MNUBINS * ioncount; i++) { - spectra.timesteps[nts].absorption[i] = 0; - } - } + for (int nts = 0; nts < globals::ntimesteps; nts++) { + spectra.timesteps[nts].flux = &spectra.fluxalltimesteps[nts * MNUBINS]; } -} - -static void alloc_emissionabsorption_spectra(auto &spectra) { - size_t mem_usage = 0; - const int proccount = get_proccount(); - spectra->do_emission_res = true; - mem_usage += globals::ntstep * MNUBINS * get_nelements() * get_max_nions() * sizeof(double); - spectra->absorptionalltimesteps = - std::make_unique(globals::ntstep * MNUBINS * get_nelements() * get_max_nions()); - assert_always(spectra->absorptionalltimesteps != nullptr); - - mem_usage += 2 * globals::ntstep * MNUBINS * proccount * sizeof(double); - spectra->emissionalltimesteps = std::make_unique(globals::ntstep * MNUBINS * proccount); - assert_always(spectra->emissionalltimesteps != nullptr); - - spectra->trueemissionalltimesteps = std::make_unique(globals::ntstep * MNUBINS * proccount); - assert_always(spectra->trueemissionalltimesteps != nullptr); - - for (int nts = 0; nts < globals::ntstep; nts++) { - assert_always(spectra->timesteps[nts].absorption == nullptr); - assert_always(spectra->timesteps[nts].emission == nullptr); - assert_always(spectra->timesteps[nts].trueemission == nullptr); - - spectra->timesteps[nts].absorption = - &spectra->absorptionalltimesteps[nts * MNUBINS * get_nelements() * get_max_nions()]; - - spectra->timesteps[nts].emission = &spectra->emissionalltimesteps[nts * MNUBINS * proccount]; - - spectra->timesteps[nts].trueemission = &spectra->trueemissionalltimesteps[nts * MNUBINS * proccount]; - - assert_always(spectra->timesteps[nts].absorption != nullptr); - assert_always(spectra->timesteps[nts].emission != nullptr); - assert_always(spectra->timesteps[nts].trueemission != nullptr); - } - printout("[info] mem_usage: allocated set of emission/absorption spectra occupying total of %.3f MB (nnubins %d)\n", - mem_usage / 1024. / 1024., MNUBINS); -} - -auto alloc_spectra(const bool do_emission_res) -> std::unique_ptr { - size_t mem_usage = 0; - assert_always(globals::ntstep > 0); - // std::unique_ptr spectra(new struct spec); - auto spectra = std::make_unique(); - mem_usage += globals::ntstep * sizeof(struct spec); + if (do_emission_res) { + const int proccount = get_proccount(); - spectra->do_emission_res = false; // might be set true later by alloc_emissionabsorption_spectra - spectra->lower_freq = std::make_unique(MNUBINS); - spectra->delta_freq = std::make_unique(MNUBINS); + mem_usage += globals::ntimesteps * MNUBINS * get_nelements() * get_max_nions() * sizeof(double); + mem_usage += 2 * globals::ntimesteps * MNUBINS * proccount * sizeof(double); - spectra->timesteps = std::make_unique(globals::ntstep); - mem_usage += globals::ntstep * sizeof(struct timestepspec); + spectra.absorptionalltimesteps.resize(globals::ntimesteps * MNUBINS * get_nelements() * get_max_nions()); + spectra.emissionalltimesteps.resize(globals::ntimesteps * MNUBINS * proccount); + spectra.trueemissionalltimesteps.resize(globals::ntimesteps * MNUBINS * proccount); - spectra->fluxalltimesteps = std::make_unique(globals::ntstep * MNUBINS); - mem_usage += globals::ntstep * MNUBINS * sizeof(double); + std::ranges::fill(spectra.absorptionalltimesteps, 0.0); + std::ranges::fill(spectra.emissionalltimesteps, 0.0); + std::ranges::fill(spectra.trueemissionalltimesteps, 0.0); - assert_always(MNUBINS > 0); - for (int nts = 0; nts < globals::ntstep; nts++) { - spectra->timesteps[nts].flux = &spectra->fluxalltimesteps[nts * MNUBINS]; + for (size_t nts = 0; nts < spectra.timesteps.size(); nts++) { + spectra.timesteps[nts].absorption = + &spectra.absorptionalltimesteps[nts * MNUBINS * get_nelements() * get_max_nions()]; + spectra.timesteps[nts].emission = &spectra.emissionalltimesteps[nts * MNUBINS * proccount]; + spectra.timesteps[nts].trueemission = &spectra.trueemissionalltimesteps[nts * MNUBINS * proccount]; + } + if (print_memusage) { + printout("[info] mem_usage: set of emission/absorption spectra occupy %.3f MB (nnubins %d)\n", + mem_usage / 1024. / 1024., MNUBINS); + } - spectra->timesteps[nts].absorption = nullptr; - spectra->timesteps[nts].emission = nullptr; - spectra->timesteps[nts].trueemission = nullptr; - } + } else { + for (int nts = 0; nts < globals::ntimesteps; nts++) { + spectra.timesteps[nts].absorption = nullptr; + spectra.timesteps[nts].emission = nullptr; + spectra.timesteps[nts].trueemission = nullptr; + } - printout("[info] mem_usage: allocated set of spectra occupying total of %.3f MB (nnubins %d)\n", - mem_usage / 1024. / 1024., MNUBINS); + spectra.absorptionalltimesteps.clear(); + spectra.emissionalltimesteps.clear(); + spectra.trueemissionalltimesteps.clear(); - if (do_emission_res) { - alloc_emissionabsorption_spectra(spectra); + if (print_memusage) { + printout("[info] mem_usage: set of spectra occupy %.3f MB (nnubins %d)\n", mem_usage / 1024. / 1024., MNUBINS); + } } - - return spectra; } -void add_to_spec_res(const struct packet *const pkt_ptr, int current_abin, struct spec &spectra, struct spec *stokes_i, - struct spec *stokes_q, struct spec *stokes_u) +void add_to_spec_res(const struct packet *const pkt_ptr, int current_abin, struct spec &spectra, + const struct spec *stokes_i, const struct spec *stokes_q, const struct spec *stokes_u) // Routine to add a packet to the outgoing spectrum. { // Need to (1) decide which time bin to put it in and (2) which frequency bin. @@ -620,18 +557,18 @@ void add_to_spec_res(const struct packet *const pkt_ptr, int current_abin, struc } #ifdef MPI_ON -static void mpi_reduce_spectra(int my_rank, struct spec *spectra, int numtimesteps) { +static void mpi_reduce_spectra(int my_rank, struct spec &spectra, int numtimesteps) { for (int n = 0; n < numtimesteps; n++) { - MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra->timesteps[n].flux, spectra->timesteps[n].flux, MNUBINS, - MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra.timesteps[n].flux, spectra.timesteps[n].flux, MNUBINS, MPI_DOUBLE, + MPI_SUM, 0, MPI_COMM_WORLD); - if (spectra->do_emission_res) { + if (spectra.do_emission_res) { const int proccount = get_proccount(); - MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra->timesteps[n].absorption, spectra->timesteps[n].absorption, + MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra.timesteps[n].absorption, spectra.timesteps[n].absorption, MNUBINS * get_nelements() * get_max_nions(), MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra->timesteps[n].emission, spectra->timesteps[n].emission, + MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra.timesteps[n].emission, spectra.timesteps[n].emission, MNUBINS * proccount, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra->timesteps[n].trueemission, spectra->timesteps[n].trueemission, + MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : spectra.timesteps[n].trueemission, spectra.timesteps[n].trueemission, MNUBINS * proccount, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); } } @@ -641,52 +578,43 @@ static void mpi_reduce_spectra(int my_rank, struct spec *spectra, int numtimeste void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) { const time_t time_func_start = time(nullptr); - std::vector rpkt_light_curve_lum(globals::ntstep, 0.); - std::vector rpkt_light_curve_lumcmf(globals::ntstep, 0.); - std::vector gamma_light_curve_lum(globals::ntstep, 0.); - std::vector gamma_light_curve_lumcmf(globals::ntstep, 0.); + std::vector rpkt_light_curve_lum(globals::ntimesteps, 0.); + std::vector rpkt_light_curve_lumcmf(globals::ntimesteps, 0.); + std::vector gamma_light_curve_lum(globals::ntimesteps, 0.); + std::vector gamma_light_curve_lumcmf(globals::ntimesteps, 0.); TRACE_EMISSION_ABSORPTION_REGION_ON = false; bool do_emission_res = WRITE_PARTIAL_EMISSIONABSORPTIONSPEC ? globals::do_emission_res : false; - if (rpkt_spectra == nullptr) { - rpkt_spectra = alloc_spectra(do_emission_res); - assert_always(rpkt_spectra != nullptr); - } - - struct spec *stokes_i = nullptr; - struct spec *stokes_q = nullptr; - struct spec *stokes_u = nullptr; - // the emission resolved spectra are slow to generate, so only allow making them for the final timestep or every n if (WRITE_PARTIAL_EMISSIONABSORPTIONSPEC && globals::do_emission_res) { - if ((nts >= globals::ftstep - 1) || (nts % 5 == 0)) { + if ((nts >= globals::timestep_finish - 1) || (nts % 5 == 0)) { do_emission_res = true; } } - init_spectra(*rpkt_spectra, NU_MIN_R, NU_MAX_R, do_emission_res); + init_spectra(rpkt_spectra, NU_MIN_R, NU_MAX_R, do_emission_res); for (int ii = 0; ii < globals::npkts; ii++) { if (pkts[ii].type == TYPE_ESCAPE) { const int abin = -1; // all angles if (pkts[ii].escape_type == TYPE_RPKT) { - add_to_lc_res(&pkts[ii], abin, rpkt_light_curve_lum.data(), rpkt_light_curve_lumcmf.data()); - add_to_spec_res(&pkts[ii], abin, *rpkt_spectra, stokes_i, stokes_q, stokes_u); + add_to_lc_res(&pkts[ii], abin, rpkt_light_curve_lum, rpkt_light_curve_lumcmf); + add_to_spec_res(&pkts[ii], abin, rpkt_spectra, nullptr, nullptr, nullptr); } else if (abin == -1 && pkts[ii].escape_type == TYPE_GAMMA) { - add_to_lc_res(&pkts[ii], abin, gamma_light_curve_lum.data(), gamma_light_curve_lumcmf.data()); + add_to_lc_res(&pkts[ii], abin, gamma_light_curve_lum, gamma_light_curve_lumcmf); } } } const int numtimesteps = nts + 1; // only produce spectra and light curves up to one past nts - assert_always(numtimesteps <= globals::ntstep); + assert_always(numtimesteps <= globals::ntimesteps); const time_t time_mpireduction_start = time(nullptr); #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); - mpi_reduce_spectra(my_rank, rpkt_spectra.get(), numtimesteps); + mpi_reduce_spectra(my_rank, rpkt_spectra, numtimesteps); MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : rpkt_light_curve_lum.data(), rpkt_light_curve_lum.data(), numtimesteps, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Reduce(my_rank == 0 ? MPI_IN_PLACE : rpkt_light_curve_lumcmf.data(), rpkt_light_curve_lumcmf.data(), numtimesteps, @@ -700,10 +628,9 @@ void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) const time_t time_mpireduction_end = time(nullptr); if (my_rank == 0) { - write_light_curve("light_curve.out", -1, rpkt_light_curve_lum.data(), rpkt_light_curve_lumcmf.data(), numtimesteps); - write_light_curve("gamma_light_curve.out", -1, gamma_light_curve_lum.data(), gamma_light_curve_lumcmf.data(), - numtimesteps); - write_spectrum("spec.out", "emission.out", "emissiontrue.out", "absorption.out", *rpkt_spectra, numtimesteps); + write_light_curve("light_curve.out", -1, rpkt_light_curve_lum, rpkt_light_curve_lumcmf, numtimesteps); + write_light_curve("gamma_light_curve.out", -1, gamma_light_curve_lum, gamma_light_curve_lumcmf, numtimesteps); + write_spectrum("spec.out", "emission.out", "emissiontrue.out", "absorption.out", rpkt_spectra, numtimesteps); } #ifdef MPI_ON diff --git a/spectrum.h b/spectrum.h index 3164231d8..24a5a7f2f 100644 --- a/spectrum.h +++ b/spectrum.h @@ -3,6 +3,7 @@ #include #include +#include struct timestepspec { double *flux = nullptr; @@ -14,27 +15,27 @@ struct timestepspec { struct spec { double nu_min = -1.; double nu_max = -1.; - std::unique_ptr lower_freq = nullptr; - std::unique_ptr delta_freq = nullptr; - std::unique_ptr fluxalltimesteps = nullptr; - std::unique_ptr absorptionalltimesteps = nullptr; - std::unique_ptr emissionalltimesteps = nullptr; - std::unique_ptr trueemissionalltimesteps = nullptr; - std::unique_ptr timesteps = nullptr; - bool do_emission_res = true; + std::vector lower_freq; + std::vector delta_freq; + std::vector fluxalltimesteps; + std::vector absorptionalltimesteps; + std::vector emissionalltimesteps; + std::vector trueemissionalltimesteps; + std::vector timesteps; + bool do_emission_res = false; }; -void write_spectrum(const std::string &spec_filename, const char *emission_filename, const char *trueemission_filename, - const char *absorption_filename, const struct spec &spectra, int numtimesteps); +void write_spectrum(const std::string &spec_filename, const std::string &emission_filename, + const std::string &trueemission_filename, const std::string &absorption_filename, + const struct spec &spectra, int numtimesteps); void write_specpol(const std::string &specpol_filename, const std::string &emission_filename, - const std::string &absorption_filename, struct spec *stokes_i, struct spec *stokes_q, - struct spec *stokes_u); + const std::string &absorption_filename, const struct spec *stokes_i, const struct spec *stokes_q, + const struct spec *stokes_u); -void add_to_spec_res(const struct packet *pkt_ptr, int current_abin, struct spec &spectra, struct spec *stokes_i, - struct spec *stokes_q, struct spec *stokes_u); +void add_to_spec_res(const struct packet *const pkt_ptr, int current_abin, struct spec &spectra, + const struct spec *stokes_i, const struct spec *stokes_q, const struct spec *stokes_u); -std::unique_ptr alloc_spectra(bool do_emission_res); void init_spectra(struct spec &spectra, double nu_min, double nu_max, bool do_emission_res); void init_spectrum_trace(); void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts); diff --git a/stats.cc b/stats.cc index 33f18eaa1..f991c087d 100644 --- a/stats.cc +++ b/stats.cc @@ -1,5 +1,8 @@ #include "stats.h" +#include +#include + #include "atomic.h" #include "globals.h" #include "grid.h" @@ -9,21 +12,19 @@ namespace stats { static double *ionstats = nullptr; -static int *eventstats = nullptr; +static std::array, COUNTER_COUNT> eventstats; void init() { if constexpr (TRACK_ION_STATS) { ionstats = static_cast(malloc(grid::get_npts_model() * get_includedions() * ION_STAT_COUNT * sizeof(double))); } - eventstats = static_cast(malloc(COUNTER_COUNT * sizeof(int))); } void cleanup() { if constexpr (TRACK_ION_STATS) { free(ionstats); } - free(eventstats); } void increment_ion_stats(const int modelgridindex, const int element, const int ion, enum ionstattypes ionstattype, @@ -145,7 +146,7 @@ void normalise_ion_estimators(const int mgi, const double deltat, const double d if (i < nstatcounters_ratecoeff) { // convert photon event counters into rate coefficients set_ion_stats(mgi, element, ion, static_cast(i), - ratedensity / ionstagepop(mgi, element, ion)); + ratedensity / get_nnion(mgi, element, ion)); } else { set_ion_stats(mgi, element, ion, static_cast(i), ratedensity); } @@ -157,7 +158,7 @@ void normalise_ion_estimators(const int mgi, const double deltat, const double d void increment(enum eventcounters i) { assert_testmodeonly(i >= 0); assert_testmodeonly(i < COUNTER_COUNT); - safeincrement(eventstats[i]); + eventstats[i]++; } void pkt_action_counters_reset() { @@ -175,7 +176,7 @@ auto get_counter(enum eventcounters i) -> int { } void pkt_action_counters_printout(const struct packet *const pkt, const int nts) { - long allpktinteractions = 0; + u_int64_t allpktinteractions = 0; for (int i = 0; i < globals::npkts; i++) { assert_always(pkt[i].interactions >= 0); allpktinteractions += pkt[i].interactions; @@ -183,10 +184,10 @@ void pkt_action_counters_printout(const struct packet *const pkt, const int nts) const double meaninteractions = static_cast(allpktinteractions) / globals::npkts; printout("mean number of interactions per packet = %g\n", meaninteractions); - const double deltat = globals::time_step[nts].width; + const double deltat = globals::timesteps[nts].width; double modelvolume = 0.; for (int mgi = 0; mgi < grid::get_npts_model(); mgi++) { - modelvolume += grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::time_step[nts].mid / globals::tmin, 3); + modelvolume += grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::timesteps[nts].mid / globals::tmin, 3); } /// Printout packet statistics diff --git a/stats.h b/stats.h index 9435cef34..2649be66c 100644 --- a/stats.h +++ b/stats.h @@ -12,7 +12,6 @@ enum ionstattypes { ION_RADRECOMB_MACROATOM = 0, ION_RADRECOMB_KPKT = 1, ION_RADRECOMB_ABSORBED = 2, - ION_RADRECOMB_ESCAPED = 3, ION_BOUNDBOUND_MACROATOM = 4, ION_BOUNDBOUND_ABSORBED = 5, ION_NTION = 6, diff --git a/tests/classicmode_inputfiles/abundances.txt b/tests/classicmode_1d_3dgrid_inputfiles/abundances.txt similarity index 100% rename from tests/classicmode_inputfiles/abundances.txt rename to tests/classicmode_1d_3dgrid_inputfiles/abundances.txt diff --git a/tests/classicmode_inputfiles/compositiondata.txt b/tests/classicmode_1d_3dgrid_inputfiles/compositiondata.txt similarity index 100% rename from tests/classicmode_inputfiles/compositiondata.txt rename to tests/classicmode_1d_3dgrid_inputfiles/compositiondata.txt diff --git a/tests/classicmode_inputfiles/input-newrun.txt b/tests/classicmode_1d_3dgrid_inputfiles/input-newrun.txt similarity index 92% rename from tests/classicmode_inputfiles/input-newrun.txt rename to tests/classicmode_1d_3dgrid_inputfiles/input-newrun.txt index f4145f264..b70982c5b 100644 --- a/tests/classicmode_inputfiles/input-newrun.txt +++ b/tests/classicmode_1d_3dgrid_inputfiles/input-newrun.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -50 # globals::ntstep: number of timesteps -000 036 # itstep ftstep: number of start and end time step +50 # globals::ntimesteps: number of timesteps +000 036 # timestep_start timestep_finish: number of start and end time step 3 30 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/classicmode_inputfiles/input-resume.txt b/tests/classicmode_1d_3dgrid_inputfiles/input-resume.txt similarity index 92% rename from tests/classicmode_inputfiles/input-resume.txt rename to tests/classicmode_1d_3dgrid_inputfiles/input-resume.txt index bc8a0edcb..538497ae2 100644 --- a/tests/classicmode_inputfiles/input-resume.txt +++ b/tests/classicmode_1d_3dgrid_inputfiles/input-resume.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -50 # globals::ntstep: number of timesteps -035 050 # itstep ftstep: number of start and end time step +50 # globals::ntimesteps: number of timesteps +035 050 # timestep_start timestep_finish: number of start and end time step 3 30 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/classicmode_inputfiles/model.txt b/tests/classicmode_1d_3dgrid_inputfiles/model.txt similarity index 100% rename from tests/classicmode_inputfiles/model.txt rename to tests/classicmode_1d_3dgrid_inputfiles/model.txt diff --git a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..432d20e55 --- /dev/null +++ b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,25 @@ +43bf9f48a0f511482f5fab8a05d204d4 absorption.out +689de69d0631b260607f632cefa3a312 absorptionpol.out +c0604236aa7df99cda43e5c7bbe7b6f4 bflist.out +7c861e2528b3e7066bee6d1eb09b2623 deposition.out +635759cfc84035d2152ad72fe25a8089 emission.out +fe1d5ff2dc8076a20e087d8a880a57a5 emissionpol.out +03199910aa88c84f89b1bab3aab547e1 emissiontrue.out +e9a3293583a923eaf94d04f4b71bacc1 gamma_light_curve.out +3f823170e969aad8252d685a2284f70a gamma_spec.out +057b226c371f3819cba5e04bfea3d114 gammalinelist.out +20bddb22b6f084a7abc105c210366bfd grid.out +27e9af272dcd0b6a9bd03966e35195c7 light_curve.out +5740f40190d45653e5fce57ddf577dc8 linestat.out +c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out +b5585182cae11527810cfb82072fb919 packets00_0000.out +3595e9d4484a55eb1d40d1f44fd168fc packets00_0001.out +e3e8093504b5f4ceb4b23924e750f0b2 spec.out +bbf9c836a1bbc64f7919d8c2dccb6b52 specpol.out +040c71f981716a2abf0467a82b1ac13c timesteps.out +08647501befa081004acc883edb7898a vpkt_grid_0-0.out +b2a6c81cfd8605508b89ed47f1f83be6 vpkt_grid_1-0.out +8d19f1a64dd5cb69869048df175a8a02 vspecpol_0-0.out +804848d2d65244f3c5d481db0384213b vspecpol_1-0.out +263568b30ce0c2633fbec2157aaa1114 job1/estimators_0000.out +b2ebf78e08410d7c7562d0d06120c95e job1/estimators_0001.out diff --git a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..8e9004e13 --- /dev/null +++ b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,21 @@ +fd071bf164378576c70bc25583f0eadb absorption.out +c0604236aa7df99cda43e5c7bbe7b6f4 bflist.out +776e733e780e8fb7509a5b3578bad22a deposition.out +f9368d553c9ce47d2a653fb8d7cd8840 emission.out +d1bfa2500e1516981f0bddf42c8a6038 emissiontrue.out +94f7bdd6bf7c8d2f30e0beb08280805c gamma_light_curve.out +057b226c371f3819cba5e04bfea3d114 gammalinelist.out +20bddb22b6f084a7abc105c210366bfd grid.out +5624f2243562637cd6d885e4b77e2867 light_curve.out +5740f40190d45653e5fce57ddf577dc8 linestat.out +c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out +1d1e1bba1dc6890d01ec086506017bcf packets00_0000.out +13c47cc21b83945e9f0711328825ff42 packets00_0001.out +814474a12997e69688761d0836d3e98a spec.out +040c71f981716a2abf0467a82b1ac13c timesteps.out +99c590d59e35705e779c220134f7a33a vpkt_grid_0-0.out +35ce3462e5b9493bde385b8bad9ea954 vpkt_grid_1-0.out +2ed5cc4ea567b624eb8a4cb9137a2e01 vspecpol_0-0.out +01fd53e3ca382c4e2b0f5b588edbd9e5 vspecpol_1-0.out +2c67a882e73f240ecfeffa05f04d8c5f job0/estimators_0000.out +33ea538a70082b2a2ae43ae71569fc3e job0/estimators_0001.out diff --git a/tests/classicmode_inputfiles/syn_dir.txt b/tests/classicmode_1d_3dgrid_inputfiles/syn_dir.txt similarity index 100% rename from tests/classicmode_inputfiles/syn_dir.txt rename to tests/classicmode_1d_3dgrid_inputfiles/syn_dir.txt diff --git a/tests/classicmode_1d_3dgrid_inputfiles/vpkt.txt b/tests/classicmode_1d_3dgrid_inputfiles/vpkt.txt new file mode 100644 index 000000000..46343881e --- /dev/null +++ b/tests/classicmode_1d_3dgrid_inputfiles/vpkt.txt @@ -0,0 +1,14 @@ +2 +1 0 +0 0 +1 12 0 -1 1 2 6 8 14 16 20 26 27 28 +0 10 30 +1 1 3500 10000 +1 100 +10 +1 +11.5 21.5 +2 3500 6000 6400 7200 + + + diff --git a/tests/classicmode_3d_inputfiles/input-newrun.txt b/tests/classicmode_3d_inputfiles/input-newrun.txt index 8849e0e37..569806299 100644 --- a/tests/classicmode_3d_inputfiles/input-newrun.txt +++ b/tests/classicmode_3d_inputfiles/input-newrun.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -30 # globals::ntstep: number of timesteps -000 011 # itstep ftstep: number of start and end time step +30 # globals::ntimesteps: number of timesteps +000 011 # timestep_start timestep_finish: number of start and end time step 3 8 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/classicmode_3d_inputfiles/input-resume.txt b/tests/classicmode_3d_inputfiles/input-resume.txt index 33ca4a2fb..d92fa54bb 100644 --- a/tests/classicmode_3d_inputfiles/input-resume.txt +++ b/tests/classicmode_3d_inputfiles/input-resume.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -30 # globals::ntstep: number of timesteps -010 030 # itstep ftstep: number of start and end time step +30 # globals::ntimesteps: number of timesteps +010 030 # timestep_start timestep_finish: number of start and end time step 3 8 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/classicmode_3d_inputfiles/results_md5_final.txt b/tests/classicmode_3d_inputfiles/results_md5_final.txt index ce0f19b82..4fc5b78cb 100644 --- a/tests/classicmode_3d_inputfiles/results_md5_final.txt +++ b/tests/classicmode_3d_inputfiles/results_md5_final.txt @@ -1,524 +1,524 @@ -7e563d43a4bbeb413b23bddc499c12b4 absorption.out -e86f731e121b8c635b4966478b15f138 absorption_res_00.out -892b57a14835f887e9efa013c1f527ba absorption_res_01.out -35e16bcb5ed2102b92673f47e2d1676a absorption_res_02.out -6d021f2c8576a3fdf69c4e0a9236bcb6 absorption_res_03.out -8e1b546f060dae2cde17602e3eb83711 absorption_res_04.out -561eec609c2a76cb94dba0c9bb87a6b5 absorption_res_05.out -177113cb4953ae8698402eb2c8c9c80d absorption_res_06.out -08a279bad1bd5a09e9624795ae50e8d1 absorption_res_07.out -95415868a755988d2eec9fb9156871ba absorption_res_08.out -99c54f416b3f5f1c9fd2c03300fd5fab absorption_res_09.out -56f1a0626c89c895424b64e8a6ef1626 absorption_res_10.out -b37f59d411e21206a0c2822669777373 absorption_res_11.out -303659b22c8f93e843c0ed3526b34f10 absorption_res_12.out -4fa10c58874af347cdd09e30d6592ca1 absorption_res_13.out -e8c232b89ab5adef40091f51db912dc9 absorption_res_14.out -83d274b3198378dfb23fb192fec0c766 absorption_res_15.out -d9d2e561e82710ef6d973c5bf3d45b44 absorption_res_16.out -29274e3e32e3a0f6c45f51c84bb5d9c8 absorption_res_17.out -eff917c10b1dc1e903f71a28f51c337e absorption_res_18.out -03dbd3d295b1a4ee2cc55e099ec2e7e5 absorption_res_19.out -d9cbb6d2a7769db569a18a5502ad2be7 absorption_res_20.out -db1bcf91449d49a21ec4546a69d90753 absorption_res_21.out -fdb9f870b4d3b89a81179346ea6882a3 absorption_res_22.out -29c2c57902ed1f4899d9cf2b98b2a18d absorption_res_23.out -7c882566cde067addb588edd87f0cc63 absorption_res_24.out -78fbb89e66f0fd491be7e1cbf598d304 absorption_res_25.out -ed606ce0f478f3e0d46fa48c6ab87ce2 absorption_res_26.out -432291167e8159447395f3264ff87cd3 absorption_res_27.out -fb88e7434ce9442634c2157b71c692e6 absorption_res_28.out -68a09199cb2f30e503bd7747005f692f absorption_res_29.out -7b8508e20b5a7e867b05191c228e3f35 absorption_res_30.out -7e63abec8637f9e7100e453682fd631d absorption_res_31.out -766bfbe154d1ee219a98e99e304b1cce absorption_res_32.out -51e072517cee33924c7e156d814acd49 absorption_res_33.out -9179b51b6662ac53bd42c46debb4e16b absorption_res_34.out -99819734d583f74140f74c7a35459190 absorption_res_35.out -35521e46cbca3bc62e89a580572b2973 absorption_res_36.out -4a604f072a370d0ff2a6af4996e73acb absorption_res_37.out -483f593ecfe1b22c6c69ca381a57fea1 absorption_res_38.out -200f40cc531c0fa5cb321c8ae0d75945 absorption_res_39.out -b580805d982bf7f7e2723b9721670fb2 absorption_res_40.out -fe35dc839087d60ca3a2c7b9d3b62d0b absorption_res_41.out -6a279b33aeb15e37be8ac28cd4fa65cc absorption_res_42.out -812ed9987690b98a3a73e298820064af absorption_res_43.out -f02886a5c01711a3565db82e990bf741 absorption_res_44.out -3717312b9b97f6b4979241527dc115ac absorption_res_45.out -0269b6938f16caaf98c108d22d7dee0c absorption_res_46.out -074843258fab2641ce2a15b3f29c8bde absorption_res_47.out -9e6c98ec91a1ee6cbe01e5560aee2df7 absorption_res_48.out -a969ecf9d64b524a38df9e5d1662f5aa absorption_res_49.out -dfda60ded5436fb62b00466d168f59c5 absorption_res_50.out -b28dda54ecf6c896134a09c6f7b235f7 absorption_res_51.out -3ca30e9a76d87f09584a8f5355382e4d absorption_res_52.out -83b955a53a6ba636009c421cba754da7 absorption_res_53.out -ba2f7fc71ce3997781eb8cf58d3937b5 absorption_res_54.out -4e78f8ffe442f824f26c5f9a07e87b7b absorption_res_55.out -0d7b1a051727d9fbf387260275311587 absorption_res_56.out -33c89514bd5b64d19470e673df6594bc absorption_res_57.out -5d0b4c68ae0c4aacc810f219757c38c0 absorption_res_58.out -bcab9af6ee00102662ae15c864b0e6b3 absorption_res_59.out -e302a50022f16762ca5f967c3d94e396 absorption_res_60.out -a42bbb9b0b9f2e10171f7c0554301cca absorption_res_61.out -2436dfdbe05e45f5bd424ef858354522 absorption_res_62.out -88306f7cd86bbacea7cd52a661ad2687 absorption_res_63.out -69d94be33c39e0025a678fafe23c557d absorption_res_64.out -8104fbdac589d6f54db533976c20a477 absorption_res_65.out -ed161ceae7971b4c1127a4959e11a0c9 absorption_res_66.out -889e3821a08e7dbbfcf0799d8a5b2253 absorption_res_67.out -63390cc856f1fa836896b45fb4d29eb6 absorption_res_68.out -f3150725ad30171ad0dd508e902cabd2 absorption_res_69.out -fe6050baf49db9745c0bbba2216936b2 absorption_res_70.out -ef75731d73a2d2c495aef3be9249c652 absorption_res_71.out -e4487717dadda8e6987002bf05c192a2 absorption_res_72.out -a523cb31b187b1ce9bfc895421d028ad absorption_res_73.out -13a4ebc8f8482257f38826757e0776c1 absorption_res_74.out -46bd77c9964f90d420703cbd0c9a8dee absorption_res_75.out -63c7a235863812693056ebb42f9f9337 absorption_res_76.out -83c56f2d7a10a6c0ec9c82b34cc7d163 absorption_res_77.out -7568d4135cf415cfbfb7df8c3bbac72e absorption_res_78.out -86930423049d4f9c54ab9d1f5de941c0 absorption_res_79.out -f6b3efa66677b360c07412e2903f9a99 absorption_res_80.out -304c8180cdd3a2d248501f746f0d0d09 absorption_res_81.out -64b465d465331e2b6cbe3d30c50f3a69 absorption_res_82.out -b777d09a6161e0d9a50b8285d6bf59a7 absorption_res_83.out -39546187619c8043155b13492a0609eb absorption_res_84.out -52997511ba7f651ba0d05a2794c687a3 absorption_res_85.out -9152d79771b698464e75d711886766e7 absorption_res_86.out -feb414c724d4f3bebe911d1ca43d69e8 absorption_res_87.out -8170606383c15034f244cd1fe6721615 absorption_res_88.out -700c1c67fe36b675130cb58f819ac7f5 absorption_res_89.out -d7703757d078ab4b5934cd1e4de16beb absorption_res_90.out -cdfba32cfacb259dbd685220f63e3bef absorption_res_91.out -7e39bb3bf83665b9503b6ba98ef1acc0 absorption_res_92.out -bd8e5e8104cf64a0deae6612ddbc4dbb absorption_res_93.out -3921975d5cb9abc58cdd8f9e8bd994a5 absorption_res_94.out -4c242dd2c8944d8011079dfa7faf9760 absorption_res_95.out -48d6b129ffe4e13dd2bcb7df1b60fe82 absorption_res_96.out -c1b836858994ab496b9d60eeb05cbc75 absorption_res_97.out -4f43bd8cb2f7efa6c6452a602b7f8a15 absorption_res_98.out -d0027cbb335c4f1af8649fb6f2f89532 absorption_res_99.out -7c8bf09ed9824a8e615cbfe0efa0f5ad absorptionpol.out -63b547538521f044b0b1ed12d1255850 absorptionpol_res_00.out -d01eba1b7d7f0ce0317a45f2ec49cd38 absorptionpol_res_01.out -c398f0493cbdc4c5d63b0a67848590a1 absorptionpol_res_02.out -14c9b36d6da9a1ecc0c8f59bca9d44ca absorptionpol_res_03.out -6f8653ce7f820a195bb3373cab2bc26b absorptionpol_res_04.out -7190552244e2ded2e44e1d2692bf2ea7 absorptionpol_res_05.out -0d5beb0df2501c755e6ae217e24e65d2 absorptionpol_res_06.out -3c9e3e0722cb43211a784d8ad6b798bf absorptionpol_res_07.out -1b2a058a6183e0a2052618b21a7cf0c5 absorptionpol_res_08.out -2ddf33f51d6a3104e39d8cf891ed4f5d absorptionpol_res_09.out -55c0ec8f94eec62e25bf95897a0bca89 absorptionpol_res_10.out -86b8dc2513ed8bd17b3d2230b759b09d absorptionpol_res_11.out -c098f08614fda4492baad0d5f837695c absorptionpol_res_12.out -948a9d0ddf9cfc20c59f8fc31cd20304 absorptionpol_res_13.out -4c1cbd65c409ecb158caab99c613b483 absorptionpol_res_14.out -81f6b39e6e038b649df8a10d2cf9b047 absorptionpol_res_15.out -47d42131f047a0830dc968650f596b00 absorptionpol_res_16.out -94570db237788407340fa12b89e8553b absorptionpol_res_17.out -3300488702677331fe2bf2dbeeb2eb9c absorptionpol_res_18.out -e9ce120113974e7f5224304bdfd6501a absorptionpol_res_19.out -af0133a1ac06754134be54bf3f17782b absorptionpol_res_20.out -0d1999436738947b27c971d260339b18 absorptionpol_res_21.out -f4633105a031a37924fc0a77826ae4ea absorptionpol_res_22.out -bc09445389d2c4ec955f4afb4dd010d2 absorptionpol_res_23.out -56237d9730f078684df91c558dfa215e absorptionpol_res_24.out -2ae4883a382162fc2900b7e51bfc6c7f absorptionpol_res_25.out -9c947857b9e79e18d26a7313d103b2dc absorptionpol_res_26.out -7be3a95e7d20e35dfedd140d6af7b1f0 absorptionpol_res_27.out -b7388781823985efe13821d2d883984e absorptionpol_res_28.out -1dd58cbbeaf83188e0f47ed7e1f16c2a absorptionpol_res_29.out -2de6a3de1c79a011e3772146524a6c53 absorptionpol_res_30.out -4f8a5cc6380a2371e62eddd338bfcd87 absorptionpol_res_31.out -4b5f3f2a517a64a8372af39f5a3f95a6 absorptionpol_res_32.out -39ae2c7a5bc5b0289e58c630f933273f absorptionpol_res_33.out -1da50b3fb27a4c0e9cb72d8e58fa522d absorptionpol_res_34.out -7bd58b4f8f0d39027ab9bd4dae17d196 absorptionpol_res_35.out -3ca18ac75a42ad033e20ba9ccb8550aa absorptionpol_res_36.out -a0c224c6090c8d670b635246c8f9030c absorptionpol_res_37.out -b19d270fc00382a49cc1e2560cc206ba absorptionpol_res_38.out -b08fec8d8319e6ceeaa5527f14fa9da3 absorptionpol_res_39.out -d792eed8b4866962656c858f88719cca absorptionpol_res_40.out -890336d5f5629c5075eba31b06233804 absorptionpol_res_41.out -22e8c1b9a6da5cb13cd74c91039d4511 absorptionpol_res_42.out -110f5c381cc56c9b682899b7853a3fe7 absorptionpol_res_43.out -9b6331679aa3df7293bd0892e9fbcced absorptionpol_res_44.out -970ad3f2ab426cae6c26fdb7d0a0dc11 absorptionpol_res_45.out -2406fa083a5882d8fbb2984d8139eed4 absorptionpol_res_46.out -c72076d4e90777331cb01bb8603e1bc2 absorptionpol_res_47.out -1a9f74fb2f60b44077ef0a0b77462528 absorptionpol_res_48.out -af8ca8c45249ec75f604a1d0ca7c8b76 absorptionpol_res_49.out -6e1fd5534eff933747c0430ccee91677 absorptionpol_res_50.out -05c64bf15480c80d67c3a07bcefd2369 absorptionpol_res_51.out -918814ec949ac27278196a0a788dd7c5 absorptionpol_res_52.out -c0f06578c13fe810d355ebe727240ea7 absorptionpol_res_53.out -d68f82434fa671dba7e88a3081e31d1b absorptionpol_res_54.out -88f076a2c81b1af0abd8c96712c4ebe2 absorptionpol_res_55.out -4d361b3f2623c0dbe4acaa9bfb71639b absorptionpol_res_56.out -a74d509826e3b5ea764e88538df5573f absorptionpol_res_57.out -1d6011984e38ba210d032ac29833d811 absorptionpol_res_58.out -a804a35e0886196a3205f1bfc5222598 absorptionpol_res_59.out -8c0a1917b76b8d3e9e15b27f9f1b1058 absorptionpol_res_60.out -999e4fa1c0873654f554c33c91b636c8 absorptionpol_res_61.out -c09b5896bbdb1271abffdea3860d072a absorptionpol_res_62.out -f38f5b412846da3738f03b7e8c77c358 absorptionpol_res_63.out -bb0717bbfce7cb64a812a57519cc6802 absorptionpol_res_64.out -87ca7cd77788c0ef9c4f291210867e83 absorptionpol_res_65.out -6171719e0add351f13b74315525e8d85 absorptionpol_res_66.out -147008c3c70f6794323293b5937b9093 absorptionpol_res_67.out -86913336f24b22aa8474ce5e0b3e65dc absorptionpol_res_68.out -3c9759b4497bddbb4b4ea51ebac60e6e absorptionpol_res_69.out -bfe120e8703376067d10643460f3f2fd absorptionpol_res_70.out -8a008fa3c44c239aae1f3036ccc1d5d5 absorptionpol_res_71.out -7ef2490895355a64e342b159aa62441a absorptionpol_res_72.out -f840f970e8d983eefb82a0602e441afc absorptionpol_res_73.out -5aab7de08ab6e5d6ab4297943a6d0249 absorptionpol_res_74.out -78304ae9636f4db5bf5225f1dba1891f absorptionpol_res_75.out -130f105f73774980d92ffe261c6aa1be absorptionpol_res_76.out -c1f6627ef0cb0e87ca3fc56a500d5526 absorptionpol_res_77.out -2eb6f54a796aab0bae4bb2496f5c62af absorptionpol_res_78.out -71a4d4e5b57e8b38ffe38f72ed697fd6 absorptionpol_res_79.out -6b376df12683e0e1ea819b81f0bb27fa absorptionpol_res_80.out -ccc5ea11c5a6824b09160e3731bde843 absorptionpol_res_81.out -c6890555ec301a03b11bcef6612aa91a absorptionpol_res_82.out -dd65fb19d61a06af236d55f8e225833e absorptionpol_res_83.out -fe2a4f8d5822656f0ce7089f2e6f1bb9 absorptionpol_res_84.out -ba08df401ecbdae9ac0bba28824493b0 absorptionpol_res_85.out -fd782f9bb8ee167b862ca847947486e6 absorptionpol_res_86.out -6920d2db75540a0a220f2212ce9e83a9 absorptionpol_res_87.out -26c64bcc9936447d994c53812c7ac613 absorptionpol_res_88.out -782255cde161074add33b900220b3440 absorptionpol_res_89.out -d9f65855e48177e9bbee78eee98cca4f absorptionpol_res_90.out -3fc1b1b5298d164f2645d960bd7fbae5 absorptionpol_res_91.out -2e12d18ea0c874bb1f6233a41429c19f absorptionpol_res_92.out -f10a91afb90ee363cf372c063e311f05 absorptionpol_res_93.out -575d6b42751763427ad997beaa734cbc absorptionpol_res_94.out -8c6cc47fdfe54727c4e313ff7194a689 absorptionpol_res_95.out -216c73404d088fc3bba7ea5f05156f25 absorptionpol_res_96.out -17ca87e194b86a7fe06010ddafc21fc1 absorptionpol_res_97.out -1e177cf4dea22aac872022ce521efec3 absorptionpol_res_98.out -209169a97813cb47f385218a619e7660 absorptionpol_res_99.out +af66a263b044c4166338608b98933ed0 absorption.out +d06fea586b692072daed1c8c4ab321a6 absorption_res_00.out +2f5435323fc0bdd6fe16b3e21592b2d7 absorption_res_01.out +9a56621395226f6d02bf5e73cf7f0bf7 absorption_res_02.out +e9cb980a30a2b6b2498ef3543c6cbbd7 absorption_res_03.out +619180ab91f262d9db621e3c3d2db12f absorption_res_04.out +43c28f5ab0df7b610ea06a3e632cbfa6 absorption_res_05.out +0e3282c5b03e29f462be2ae84775174d absorption_res_06.out +ac810093dd51adc189e6dbb293a05da3 absorption_res_07.out +27995ce183189f8226022d0465dc6fe9 absorption_res_08.out +99ab488db1912773f7b47ced030d252b absorption_res_09.out +e8aeaa225ff3b4b4de143c5840cc05a3 absorption_res_10.out +4f4f1804ad70aeb73b5f7d6cca969f02 absorption_res_11.out +2bc95e00f720c7382d22113da6fdb07d absorption_res_12.out +f0717b34b6f33d5eae5772e13fe65d85 absorption_res_13.out +589c4260c892b82b03a438ac018c5b35 absorption_res_14.out +1e48f727baabc35dbe40d02a702d31c1 absorption_res_15.out +b11b4c704c4f062b5f4bf7c0e461e4a3 absorption_res_16.out +52444caad2509a681c38a412d283f7e4 absorption_res_17.out +5f683a0c4d53cfc8b292c6983ae4d858 absorption_res_18.out +0c58ea6ff912b64a3bf65309311a250f absorption_res_19.out +d3df33e632bb0e6029b1729aa1f7e4fe absorption_res_20.out +f57067255bf65ca01e4a7c989a32e740 absorption_res_21.out +d1a805a38768062111d588d65c9f3792 absorption_res_22.out +5ed5720f8d024b491fd7c71f68dad2f2 absorption_res_23.out +133523a18df44b21aed0ef133518a127 absorption_res_24.out +bb6bcdd367f5d7aa9b54fea9ef2a0fb2 absorption_res_25.out +0ad97df0b2d163bc51746c916fb4820a absorption_res_26.out +a9465bcc39a29be9808853cb383c90c1 absorption_res_27.out +078ee3acd411ab6d909669ffc66c76e6 absorption_res_28.out +b65ab3e2df3ce02ecc9e8b5f6b916376 absorption_res_29.out +c430775ebfd2bae76ce26a15dd4c9e3f absorption_res_30.out +8dcb8523569d01bb8e5e4cc41a1a650e absorption_res_31.out +3803355524dd1418b8c66fc332196794 absorption_res_32.out +7e6ae48bf7c3f9b4c85a839cd4ddc0b5 absorption_res_33.out +468523034147eca758d02a68f1748303 absorption_res_34.out +313887fa364a72b8fc926578f4b05680 absorption_res_35.out +9fefba1537fa2747b0ca3620925f9dea absorption_res_36.out +80171bc2fc6dcf492d46bc3724aec0af absorption_res_37.out +965135406b249fd89432905372b9f5fb absorption_res_38.out +10d89e1ba8a96055f53eb3475cb7a6d9 absorption_res_39.out +3defb5369efd915fe5898ec87ef57e7f absorption_res_40.out +abb71f9ee0e1a96acff0ee9804de80a7 absorption_res_41.out +63ba34fa115faf76086226733171be41 absorption_res_42.out +8f8553df2f45f278d1459b98a21fc219 absorption_res_43.out +d1b671f2cc450532c1bf3a9e576073dd absorption_res_44.out +6cf78f0b9bd8b2a1e7ca0101ad9076a8 absorption_res_45.out +b059821cc166d52420276992e11e1b8b absorption_res_46.out +cd6ff853cb0de22ab9988d310eb5db75 absorption_res_47.out +ca789515db06dc4775c3898509a85e7b absorption_res_48.out +a36353a3954f5261b60fe9d0bcd7ed76 absorption_res_49.out +ffcbfc7a2c9b9545107665b2f2c40d3a absorption_res_50.out +81c52db5c5336635242d0d63cdea9294 absorption_res_51.out +6cf99d2ca390412ffffa6c162303656b absorption_res_52.out +22a7dedfd27b267dc8c2b06591790d10 absorption_res_53.out +b4ca43ca8bfea7a1fa305a284d55fe9f absorption_res_54.out +0f7c6bbd2ea2fae5e3cb5eaff4801166 absorption_res_55.out +7f2f707b8da24d8a03023ae77859ebf7 absorption_res_56.out +0a416f3691a8dbae6617e395a71b8939 absorption_res_57.out +e6a021b7eedea2f746e4a92b345bdd44 absorption_res_58.out +2cd47a787274e56b34df4f793d6ec64a absorption_res_59.out +c208559ba07bff4387ebc9a78da780ad absorption_res_60.out +0423888bf260a3d5c5c1b2a0affbe136 absorption_res_61.out +60ad0a4ddba36a60d748cfffa09ab770 absorption_res_62.out +3e348f48f9c2722ea8ccd3b36f0b4b2e absorption_res_63.out +98f43ccd2584d9e878ddb79e9e039ee8 absorption_res_64.out +add983c9980bdc89d020124495f07ce6 absorption_res_65.out +ea911bce3f94ea44048e1a370aaadf04 absorption_res_66.out +9a0d728386c4f9872fe5eef4040ad52e absorption_res_67.out +e238f278a5f24fcb0de7ac368a469c31 absorption_res_68.out +bbd276d4555ef24d5663f86d465eb8b2 absorption_res_69.out +d2ac757bd34fad015309ae0db278cb7d absorption_res_70.out +13b831182086c6e989202dde8126f167 absorption_res_71.out +f824221b87585dcbd2966c92fb3b4e38 absorption_res_72.out +7a66c4baa51832202c1623dbdf261bb4 absorption_res_73.out +9393ce8e5456b9086db7e4009221af6b absorption_res_74.out +a84a404949e3d8308e4d71a34870e142 absorption_res_75.out +81d5d62924bc01d4a2fcfa4613184b35 absorption_res_76.out +63ec3466e8255bb346021c60290e2211 absorption_res_77.out +96684492c67546816e811d856b62f7c1 absorption_res_78.out +7644214532a2b3e540dc49559d0a7ec9 absorption_res_79.out +35dbe01553533e1888a8eb0d40c37a96 absorption_res_80.out +72201c637dced72d0d6197206cd9dd34 absorption_res_81.out +9650767dec673cde391dcb525ef3495a absorption_res_82.out +8d5f59032ce377175982d35058d571f1 absorption_res_83.out +767ac83af929fe103f9fafb2d3074f19 absorption_res_84.out +c35628b306c6e132f2420b24e9833360 absorption_res_85.out +e303e129003638423d7b36ea5935e48e absorption_res_86.out +16290ff89efe890a64c02bd06c262af5 absorption_res_87.out +010bcdef6a90211800e3170d8d3b86fc absorption_res_88.out +045dbcfa9db8715d4395a2af30fc4d0a absorption_res_89.out +7e00e242493f2a00535b579ee69317d3 absorption_res_90.out +c6b4987bf0c38509ba99b2831d27e991 absorption_res_91.out +eaae51ebf9e829951da914095de7e243 absorption_res_92.out +17e05f62447fadbe05daf8850f3a302d absorption_res_93.out +16383e098a9034939b4a27ef79e52fb9 absorption_res_94.out +0b3efb880cfc80acfea4a1d71c3e9525 absorption_res_95.out +ac00a8d646daa4013159b3f267a556ef absorption_res_96.out +76119a9fc25ef92fc19f22fbd30befb3 absorption_res_97.out +eb72ee21d547641353f772263fe0e1df absorption_res_98.out +03348dd42d19a20bdc2ca7551cfb4191 absorption_res_99.out +2879fc84a512031c54f015d6a1952905 absorptionpol.out +d6efdae466e6c1a1d4444b38a2f78640 absorptionpol_res_00.out +3262598d5c6100198ee1cdafdbd1a316 absorptionpol_res_01.out +df2e7b3895aab56126db444bb5f64788 absorptionpol_res_02.out +8bd42a5f34376095064803dfd96e6713 absorptionpol_res_03.out +76f274aacf2d555f571bbef38bc4f7ed absorptionpol_res_04.out +95e81164e75f35e153773d7fe26f37d6 absorptionpol_res_05.out +b3d0f777df219548b0cc273a785322d2 absorptionpol_res_06.out +b0b10fea6c9a86574076a296c6b0e1fe absorptionpol_res_07.out +dbb8124243b26c146a366ceaf12e3747 absorptionpol_res_08.out +3c04109c9f61fea1bacc99495a5b6ae8 absorptionpol_res_09.out +1f89c06b40fb7b9e030c545e019a6307 absorptionpol_res_10.out +8982b3cfe52e559f0ab4ff7e3099209a absorptionpol_res_11.out +1f3519821de10e6154ee8c93930e0eca absorptionpol_res_12.out +1e29e36ab09dd3fbdd29ec29053b4f33 absorptionpol_res_13.out +116869aedf224cdc50111cc39fb8ac86 absorptionpol_res_14.out +5035c423e6b18ecf35ce3b334bffeab0 absorptionpol_res_15.out +bde87a6feda3513107ae51af53c94959 absorptionpol_res_16.out +a9dab3fb91a77c4c32bb136f434ee5b6 absorptionpol_res_17.out +924de250bde26eaa8bd35ed3bad5c2c6 absorptionpol_res_18.out +0322cfb73f85d72b0e3329c171270b00 absorptionpol_res_19.out +a05a6ce859f0178e14c5ea9c489f9c25 absorptionpol_res_20.out +c8d1e5adde9374adb0ebe1ea6ca67b65 absorptionpol_res_21.out +f777635df909e31ce1ba579377d1b993 absorptionpol_res_22.out +7105dac9117a3a5b5a09e24581999bf8 absorptionpol_res_23.out +9eb9a14b9a4fd15ba2551f1b406f9859 absorptionpol_res_24.out +be61afa97eec47797c4c597548712af1 absorptionpol_res_25.out +4a8202eb8927616eb2594fc2c6f3dff3 absorptionpol_res_26.out +bf11ce0c2dbc097caf6da99ccb6368f2 absorptionpol_res_27.out +8c9234d6316ac6c19cbfb664a7ab884f absorptionpol_res_28.out +03e397312d2783aeee79ae26ca5426d1 absorptionpol_res_29.out +abfdc97782dd65ef11e7dfcc6daf781a absorptionpol_res_30.out +55887a9b9d535de1a5f65e902716d980 absorptionpol_res_31.out +e81c53fef4fc89957bc8fb42f0746bfe absorptionpol_res_32.out +4c0e6d4339f14f8c3842c5348e9131ee absorptionpol_res_33.out +4a6b4cad9820e85ee49bea305f40179a absorptionpol_res_34.out +686787673c522afcd47470672b880b5f absorptionpol_res_35.out +e9b7b07239753882998e7ceab140dc83 absorptionpol_res_36.out +17bba06533a69f90266722af5e70e477 absorptionpol_res_37.out +ddd24e15c230f5a842047fb34f8ebc36 absorptionpol_res_38.out +3bb82370f426f370a91476e6680839aa absorptionpol_res_39.out +dab34ba9737844fda78b37c31c8e441d absorptionpol_res_40.out +39891e18a8dfeef3d33e98261ac8d387 absorptionpol_res_41.out +e214d4dc0936cd98bca4007514c7fcc1 absorptionpol_res_42.out +032250b07c92204293331b13b9f97dde absorptionpol_res_43.out +911fb1336059ccba60baf0afcc38b915 absorptionpol_res_44.out +f46de11a3e5eb3c617cfe6a65b6acee4 absorptionpol_res_45.out +f8cf3feee810da14f08783e3c3b8a287 absorptionpol_res_46.out +393d62772cfb487f6e4b063c6853ce45 absorptionpol_res_47.out +0e0f270c8659b30b064391cb03a93979 absorptionpol_res_48.out +b5cc7f21ea87cf79519ce96a9fb691b4 absorptionpol_res_49.out +aaf86213fa7b0de529dbab848099b7c8 absorptionpol_res_50.out +7ef395b87d405d41e514f423dce581aa absorptionpol_res_51.out +0821cfd63f08fb8bc0b4d3982babf2ec absorptionpol_res_52.out +9021ab084e1c7f2df7c52456279e3b6c absorptionpol_res_53.out +a55ca947bb9b98519bc00fcd9e53a429 absorptionpol_res_54.out +74dd4c2ec0a7bab8f77cdf83feaf04c4 absorptionpol_res_55.out +e1936ab63397702566e55671522cf13e absorptionpol_res_56.out +491e254893a9690a7f32dccd06a1bf31 absorptionpol_res_57.out +66dcf5ea41a03f278bf7cdcf1f7d3367 absorptionpol_res_58.out +92c25fe91c5c70d72c2c20009867c4ff absorptionpol_res_59.out +5337d1947f3d31760ec07f509c938727 absorptionpol_res_60.out +36062ec7d87eec6618a42a73ea6afbc8 absorptionpol_res_61.out +787c25dc5faff56a0037d18a74827ae6 absorptionpol_res_62.out +6f6fa4626194bee6b50f0cd1bf7e0e09 absorptionpol_res_63.out +19b54f227cd231f7d49246c3407aba1c absorptionpol_res_64.out +d277917a0d86c5613ae61bf620e78416 absorptionpol_res_65.out +5549291a984f72fa3a9de1e1e659d690 absorptionpol_res_66.out +08e98f963ec1365de9eb7168868b3bde absorptionpol_res_67.out +aa0e85c2a30db6ce7b21491b5a33b467 absorptionpol_res_68.out +9c94a3f052cc8cc0389fc08d3a97bd12 absorptionpol_res_69.out +1a348e45d6ee8e738b2bd75af9483952 absorptionpol_res_70.out +7f8f466aeba20579c3fc4770c9c34c13 absorptionpol_res_71.out +6c3c4508feb9810eba585394a73b86d8 absorptionpol_res_72.out +20c8ff9d011e54109f29c043de9336b1 absorptionpol_res_73.out +c431cbe846a3d716631c2995c32a3fc9 absorptionpol_res_74.out +3f5a0176fa2af1ca85a141f5bf0f8a89 absorptionpol_res_75.out +2fb614c5eb27486e34fb4edf61db5584 absorptionpol_res_76.out +c5bb60e25121490bcd58a16c4101dac9 absorptionpol_res_77.out +1447d26390502a2ffcd1ad8e963583f8 absorptionpol_res_78.out +880de34d06dace1860021937f1f7937c absorptionpol_res_79.out +3eac559d6e67985d44c61f828c90f348 absorptionpol_res_80.out +832129e37c018c9f55a0e7e145d59d82 absorptionpol_res_81.out +43e93709de800b744c3a31d65e16d446 absorptionpol_res_82.out +4561ecca839caae1091eb617fcdfea97 absorptionpol_res_83.out +b98c306eefbdcb296416409573efe38f absorptionpol_res_84.out +a95bad4f708c810a5e56d98e32450c63 absorptionpol_res_85.out +8140c1509163887c9f2952a79881709b absorptionpol_res_86.out +3fbb09e642684e3ac15ad37e308eb71d absorptionpol_res_87.out +b021c8e597a7d9b8652718689fb84a20 absorptionpol_res_88.out +2f190e86345ed310a57f4de8f87c4591 absorptionpol_res_89.out +da8260a2cca92c8da42c13350685ad29 absorptionpol_res_90.out +37076f0b07437c2ba800cb002d9c6725 absorptionpol_res_91.out +36336d8b53de2c9f8681ccc5c299108b absorptionpol_res_92.out +ae70c3eb753ed6c25fbb825ddc3c4bad absorptionpol_res_93.out +0ecc4863d58704e350350829cf53d84c absorptionpol_res_94.out +2de6dc21be228f4bbf39aff52d5e66d4 absorptionpol_res_95.out +ebf1ac4f367790261bf860c181e0d77c absorptionpol_res_96.out +813cc06e7e62219b3ad3ba1f478ac873 absorptionpol_res_97.out +66dd3ecac672fe8c4dc786fcf84c3bcb absorptionpol_res_98.out +758116706e8cafcf3fe0b6da096993a8 absorptionpol_res_99.out 6fffc8a4adefb2119fde9ccadbff8151 bflist.out -491efef6cd9433a7ea28bbc32837cd74 deposition.out -502927544da3e8b8eea839c11ed29449 emission.out -9bc9019ea463ae7e6eece72cbf38c750 emission_res_00.out -5a7020c88d8c1cfcefe890784508e5ac emission_res_01.out -4e78327fcb930b0df0b3271f212a7afb emission_res_02.out -b14f2da2306961f02f32d9645dafff3d emission_res_03.out -16129a8798f914cce00a037c6b7571d0 emission_res_04.out -73eede2e57a2bf9a266e2ea63adf1993 emission_res_05.out -3ca81f9cd246bae0331718a6ef00c4c1 emission_res_06.out -338f9e95e2e68334a6449c48de74eac9 emission_res_07.out -07cfc7d9bf181d6ab4e1b2b9a0d7b333 emission_res_08.out -996fafd2eb8177c5eea8cca5c4bf4d66 emission_res_09.out -209da0be4412ac74c0e4e395fa8cbcd7 emission_res_10.out -50ac72b559ddb5fbe7266b664ab9f3b9 emission_res_11.out -4a7438a25d8d692bb012b96eb294f201 emission_res_12.out -0db74dd5eb1f026c43908a9748e73539 emission_res_13.out -2776c45bd44ff3a47f0a3813f98aac2e emission_res_14.out -0ad5b668081e8868855b980e7340c0e8 emission_res_15.out -de24e106ac74f19ef3248db6897c745b emission_res_16.out -81ddbafd7b364979bd7ab9b9f1926c98 emission_res_17.out -5c64416af54a4861c4d39771523f4186 emission_res_18.out -e21749629d9457175212f1e8926a0804 emission_res_19.out -fe2174f9bbefaa056eb2b464a5c0d02a emission_res_20.out -2040caeeafe922bd878b1972cb46d562 emission_res_21.out -ca55ac5ad83e620a647b33de922fd636 emission_res_22.out -b0f4240a06043926c71eb2aae6501f26 emission_res_23.out -0edabac70a085a3c9794ee3259f4b7eb emission_res_24.out -27f9eaff54cf4b543dbfa6fbb5a1e6e7 emission_res_25.out -3e168f770ab1ffef5e60975714172872 emission_res_26.out -433b682880dc74c6b052ca1924221e17 emission_res_27.out -c2ede0e309310482fa45eee48cb28773 emission_res_28.out -94b902b7397674b241c3c5f85e9e90d1 emission_res_29.out -d38129be1aef82fe50dbe462367fbab1 emission_res_30.out -22ce6774545d49f2f1002a0cc18a64d4 emission_res_31.out -9d6448c779171b29c30ef5515aae76d2 emission_res_32.out -b0f5d645f340a2b422bb09133768b9f5 emission_res_33.out -240712cc988895794464c19dad92a3f8 emission_res_34.out -eb45488d4bdc34ad758fe67a3c10623f emission_res_35.out -2be6cacbf968defc07c51d4ebfa5f855 emission_res_36.out -5424782cd71eca064f015a598325e392 emission_res_37.out -6c744cc80d75f0f72a8f4b2af7d65892 emission_res_38.out -827a6cec22fb3b949be47fd23a80fa90 emission_res_39.out -597293667fad5702d5ffc6c282ec33c7 emission_res_40.out -0272946cf8dd99a5d12e9e569b107c2b emission_res_41.out -f44dafb6472dca7a1ca4a9d37b5d8edc emission_res_42.out -29d3278fc717042e3f02cfe35e246bcf emission_res_43.out -803a8688310d5d6292691ad96dc6c3a6 emission_res_44.out -a19f51e4d2430ba689c87afe481b14ed emission_res_45.out -b91bf4d4df05d2a45cf0c121d716f7ba emission_res_46.out -38855471be3da18c66092e3e9576fa19 emission_res_47.out -ad8dd1da747daa176bb5f835bfac4dbe emission_res_48.out -369cc834a3d87f222ca3a80cbaeee4dd emission_res_49.out -1e59adc1f35466b780663850e6ea410f emission_res_50.out -f636cfcdaaffd9441cbe0031bcc3004e emission_res_51.out -81a63590bfc66b0538e95f39afdc4004 emission_res_52.out -21c34c3d7001269daa63cf69acd1c5ba emission_res_53.out -88cb47409e9fff5e52582440b2663efd emission_res_54.out -ebea2931a2289e7c30da4f6345abd351 emission_res_55.out -952d6a0ef1982f7540e9a7d071bb00bf emission_res_56.out -964565f92d92b8d159490d85a84f6e92 emission_res_57.out -0d33f8134ac0660bca5c55c803a4e05f emission_res_58.out -d788757f0ab835fb68e602fbfbb131b9 emission_res_59.out -a399bd2ccf0e164d87725b35c28f89a0 emission_res_60.out -2aa908c0244accfc8ec9b57d940d26b2 emission_res_61.out -0dcc9494958c0cd42ddc0ac093d6e206 emission_res_62.out -c6639b129b92ea85d4b41c1f39407d5f emission_res_63.out -68afb3d74230b5db9ccf61e7962d85c5 emission_res_64.out -d871149b505fa1e70e74d6f0d3334317 emission_res_65.out -204a9c4d9e005df0ed7b5165a342a347 emission_res_66.out -6c77bd1b44102665b16363e45c028e3a emission_res_67.out -4f782e6e82be65f12773b62fa50324b8 emission_res_68.out -ee7fb054309085843b888715118ccb0e emission_res_69.out -eb23c89337b7747fbb89ecc5a63864da emission_res_70.out -378fdb6f6df14f6c657c4b407f2d50a9 emission_res_71.out -70231208c959e41799b9f0b9ce88126a emission_res_72.out -114a38a82110e3fd1a409b6c65a0ebc0 emission_res_73.out -dee8878bbe7fa73943f294d06c31cf02 emission_res_74.out -db632a8aa10434db61b95ee506a713c3 emission_res_75.out -1b9cf6e92199ff5b5019f0478dd2dcbc emission_res_76.out -dd800c22b7953d9db7c49dae269f86b3 emission_res_77.out -580c1b5a18ed97d8c79e5514fa1f41f5 emission_res_78.out -5c644555484e632a7bfbf7f143171657 emission_res_79.out -580ece85a5e088592e848a678083e815 emission_res_80.out -76ec3edd8321e6d4edd261793b7382e6 emission_res_81.out -a47e0a1ceb8d9007e558d0f82e32e6bf emission_res_82.out -0b3acffbf608745c4046c5e7fec5c325 emission_res_83.out -8a739655b699368081fce94d64f1aaae emission_res_84.out -3ab4fee514bf4ea11931f0164f886ca1 emission_res_85.out -5c70b24ba9400215c95d4544defb1b9c emission_res_86.out -956d34b963b455abd9cf2ca440b579e3 emission_res_87.out -570b0e756dca86e1c65c440ecf16eaa4 emission_res_88.out -4cb482e0124def2ad46e212d098074dc emission_res_89.out -bd26e3f7406a2fb71b3254918f1db62b emission_res_90.out -73acba6fae1bd862e2a379592cfc9546 emission_res_91.out -db5e23dfa43d4b31b1eb0f4cef3d9085 emission_res_92.out -abd7d70a01364d3f28b5bb02eb1b334e emission_res_93.out -3673b94a84e2e4f110d34b256dd63564 emission_res_94.out -d864cd313ddcb8e746cf66d65e8b698c emission_res_95.out -c74fa580fa40e4215ffc60f8986b1103 emission_res_96.out -1514072732ce38276c4cf283baefbe4a emission_res_97.out -27bf4cc3b88e41ffbe139b5bee5629fd emission_res_98.out -2e98ae0e4f871197a0b97fd44b086336 emission_res_99.out -c854d6bf0ff7932e349317853b94d3fe emissionpol.out -a9bb81f227543a9678c5a9996da2cae6 emissionpol_res_00.out -b57ab7dde3f4ec287b87758a337db3fb emissionpol_res_01.out -d3d57042ee724d4364d00b24c84c3883 emissionpol_res_02.out -160bb2931abdbe9bac2b5f2b3e5e314b emissionpol_res_03.out -80c5f5db14b9c262c986bad0954ecbb1 emissionpol_res_04.out -fb261252de6d632eff3f5d9e081ffe9e emissionpol_res_05.out -137439f16c3496176856176d154620b8 emissionpol_res_06.out -f526fe69d06ea8d4be52a8a46c3b6196 emissionpol_res_07.out -7d7a3670a4b308d4b54b74392bb2d4f7 emissionpol_res_08.out -88802d98c3823b0b7561f7e424ccd2b2 emissionpol_res_09.out -91bdef72366c336125479ee64523939c emissionpol_res_10.out -ae22edea9f1ebbaafe046e5f049d4cb0 emissionpol_res_11.out -28098a21b3c5df665ec1d5e06cf16c5b emissionpol_res_12.out -254663cbf9f90565cb73ed0847ee98ac emissionpol_res_13.out -a13a79397c0694d57801b1c5a0ac7f8e emissionpol_res_14.out -8c00144c5564ca41215df454df056db5 emissionpol_res_15.out -4442d25ed0dca8e6bff225b95ac6f632 emissionpol_res_16.out -a1eb980153f8f1ee4e8499029f6f88ef emissionpol_res_17.out -c66bc44619cebc2bdad46e60e1d9f743 emissionpol_res_18.out -063803f77145879dbe9027ae69c40067 emissionpol_res_19.out -5478d946b7b65b7d3de0640418317520 emissionpol_res_20.out -c40181ae7b731cab3d0224dd06794002 emissionpol_res_21.out -ab8d766fa4aba521e7e633b1f5395289 emissionpol_res_22.out -623515565a178833d70f0f90b9339110 emissionpol_res_23.out -3d9d2f934ee5b7eaab3968e1c30e07ab emissionpol_res_24.out -ee5ff49cdfc31315801dfd3352035e5b emissionpol_res_25.out -7cb604407fc3c41668136bf69ce908b2 emissionpol_res_26.out -845a85e3fbbb110a6d388a1bc05bcc51 emissionpol_res_27.out -c1e25ed3a7179e8c03ee9e70e7eab1e4 emissionpol_res_28.out -20c7221a4462d3fd3787e8340681b70d emissionpol_res_29.out -88e6d38f94f69fd274907986dd5a9fa4 emissionpol_res_30.out -34a74463a026bfaf9b04a67d0a926bbf emissionpol_res_31.out -9ea79636c8eed2db3f2e39d3c04de44b emissionpol_res_32.out -4914223a2857823ac9d20a584464de7a emissionpol_res_33.out -639c97594c09d29986303699316fdc38 emissionpol_res_34.out -35a16aa1c1860e87eea1c71916b52f2d emissionpol_res_35.out -d2458b89ef741c18a23bb1d8267c35ef emissionpol_res_36.out -7213a06a3a5f0997771f692d6645b02c emissionpol_res_37.out -3d43c9fe5ee824f4e83140bb1f3f6774 emissionpol_res_38.out -a7200f720a6bc20ca658678bf99bcd79 emissionpol_res_39.out -df0c16f3bdf5e117ce53d9f313a26e8d emissionpol_res_40.out -af5d31cf5c79c2eee17880f389048ea2 emissionpol_res_41.out -e9abdada2c047fb32da3cece8457e097 emissionpol_res_42.out -53c0cb79e3526794c137888c5da777bf emissionpol_res_43.out -145e807898e7b24a510df0b96389a28f emissionpol_res_44.out -f0bbf89b06174faead359105d554898c emissionpol_res_45.out -f8cd7683ff155cf3922b15264db17fa7 emissionpol_res_46.out -330dc344963d7cc6164a4bdc6dc2d571 emissionpol_res_47.out -1dfb63da371b87ba460c379b919769c0 emissionpol_res_48.out -be29b9e6cbd11ecd09a7f06049b8863b emissionpol_res_49.out -853221dac3325a35f0b43741c5cf0c0b emissionpol_res_50.out -c3f1e80588e486843732f832fd4e9e6d emissionpol_res_51.out -941ebb06b6d5cb27072df2277f8417f2 emissionpol_res_52.out -1f742358d384b445ad494a93f8b862db emissionpol_res_53.out -b7494228d63f70daa526d7d3d4398611 emissionpol_res_54.out -5c46ecb2e9dd19775fed8deb1f593c84 emissionpol_res_55.out -cc14f020ac80ca71f6670a451dafaa02 emissionpol_res_56.out -30e812ad2bf82f96fdbd20376d2fa9d7 emissionpol_res_57.out -de5476120f2171dc58c30141b5cdb469 emissionpol_res_58.out -ddc23ec3025c4f2f4ddc332a671d8dab emissionpol_res_59.out -7fa0fa0ad0e23ad08533acf8e9bc9055 emissionpol_res_60.out -dd542a4a912c02fdea1b5ce612cf033b emissionpol_res_61.out -d0f54d456c777c3cb8641c58e0976ae1 emissionpol_res_62.out -f02e4977487990f6f84eaad54b7dcb38 emissionpol_res_63.out -218c9e7dbcdcd5c5a553d29d62d5bb3f emissionpol_res_64.out -2c14c0c6ecccad6b9a2e5d0e9a3cc6b6 emissionpol_res_65.out -ba29d9b51f208777c7081f798cda9f16 emissionpol_res_66.out -29253be6243329529c88e3cd9efc89c5 emissionpol_res_67.out -27af7f8f126570d6b3835dae2e07feb4 emissionpol_res_68.out -ab98b983e5c0b9eed793746988b300f5 emissionpol_res_69.out -58f815d3658f4af61142a8ae92385f10 emissionpol_res_70.out -9e580164782ae203c5268d07374773c1 emissionpol_res_71.out -022102a4fd16e5011e5f254b78cb93da emissionpol_res_72.out -6ecbac20c9a979c8a87e4903f5982bdb emissionpol_res_73.out -12e7d01abc1bd5335d7629c53af0df12 emissionpol_res_74.out -cb45d81a5ff159a44b6d206d085da41d emissionpol_res_75.out -cc72ff03a4562a2e017e813799a4523b emissionpol_res_76.out -19af2ea07db949936daa23c2cf8f5493 emissionpol_res_77.out -419b61b3466a2149bd0e603b03ff7343 emissionpol_res_78.out -d4fb609716fa7d380ee560c8e52d60e1 emissionpol_res_79.out -8f384410c499901689ae96b3e773bb91 emissionpol_res_80.out -33c9edff522d1c38d30327deaedcb41f emissionpol_res_81.out -199ccfa225852e2e4ac231d3a2d69e07 emissionpol_res_82.out -4b5160df3b57b796cb3411a04adadaa0 emissionpol_res_83.out -eeb29883da6cce8eb940584c69a7de5b emissionpol_res_84.out -b59f09c63c54dabb4c7cdbf3c4e7b9c5 emissionpol_res_85.out -bac94757b2ea6318f7627d62b52afed8 emissionpol_res_86.out -1efdd68a0f1279b4871204b1949e0e29 emissionpol_res_87.out -aa55e4693919c2c3f92eee84916011b9 emissionpol_res_88.out -8ffbf77610fe02ed83fcef1a3c14c080 emissionpol_res_89.out -a0841badf113e85f07703a5e7a41683e emissionpol_res_90.out -411dd4a47e98c615cccf448cd056a743 emissionpol_res_91.out -56dd003a364777accb5ff1aea978784d emissionpol_res_92.out -7f76159bee5976a85a3966a733a67a8b emissionpol_res_93.out -1d772385f6d29f077cd5aad03ddb1858 emissionpol_res_94.out -a7a7fe80a1b68cade6d28ee0e58da560 emissionpol_res_95.out -f6332fc5917941b01b5e51edb712d8f2 emissionpol_res_96.out -5ead47e7a66b5e0f6a134dc85273ffc6 emissionpol_res_97.out -35837b386f5464ada1811bcdde72a8e0 emissionpol_res_98.out -1a8bbbc324ec56f897b591aa53ea0ea4 emissionpol_res_99.out -0101784adda0e7ec34f1255be1b5d6f6 emissiontrue.out -a2858c2c5ae2f0f2dc7e58145f77eed5 emissiontrue_res_00.out -04fa5ce58efd2bdc84d02c83061a8d24 emissiontrue_res_01.out -58393c39eea8963ba04fecf03e5121e4 emissiontrue_res_02.out -c6b32cbffc5e2a267d53f4c10cd45ca8 emissiontrue_res_03.out -74d96f2a809873a2889b4f1d7706a8b0 emissiontrue_res_04.out -b48071fece61dabddd6436852a9780d2 emissiontrue_res_05.out -502d8e3cb6d52484875b783db3083b3f emissiontrue_res_06.out -7c530d8a06638977df7ad7eebd7d0b38 emissiontrue_res_07.out -f590ed73917938b055e2e5c28d103586 emissiontrue_res_08.out -a45b3e85d5f65355e1c13f9362e3ea5a emissiontrue_res_09.out -a26fcc19ed50d06661c8924995259d1c emissiontrue_res_10.out -9ac4401344b1eac5f73104c529909e4b emissiontrue_res_11.out -0cb9deb03959d16951a682bf1890883a emissiontrue_res_12.out -31be854a1514854e8142a68bb4581f44 emissiontrue_res_13.out -6905f5769b430d5bd5138cbbbef1886f emissiontrue_res_14.out -bde99aa9c1a07ea89d97973ecd7c95d2 emissiontrue_res_15.out -a099ec5205ee8453f67cf6202bc9cd94 emissiontrue_res_16.out -1b0913958d62bbb88df522944060d913 emissiontrue_res_17.out -c41ef62f1ca93eae4400a3c0fd014f77 emissiontrue_res_18.out -ae0171cb1d9c1eed4ec005e29c764eb5 emissiontrue_res_19.out -d95e89c2281599c68218f29c21320ff8 emissiontrue_res_20.out -c643e79fb80ae685f9246c7e2952b6f4 emissiontrue_res_21.out -52f2fdf3de99f4c4dac6586099a9c9b3 emissiontrue_res_22.out -7f800febebab6f542a2b558c5fa84089 emissiontrue_res_23.out -453ec61a14261d78d34a7dc662bf61c9 emissiontrue_res_24.out -30137c0a7cf995e92d2f9cca705c6ea6 emissiontrue_res_25.out -edec084bae0e0a08645cf8b930fcf8a2 emissiontrue_res_26.out -6101b8089b0bd78398483367878c0702 emissiontrue_res_27.out -95b18c0ae81a55bef3c0e4f8df284432 emissiontrue_res_28.out -61e10495f751364c46bbace1614f1438 emissiontrue_res_29.out -e0022e931bb66969cec4932376593203 emissiontrue_res_30.out -18338291034b9bf2bad4183887f8a4b9 emissiontrue_res_31.out -d5249b0f9a0ec8f16b08297139dd1c71 emissiontrue_res_32.out -9b46ec7401e7e851f12e911c960682fe emissiontrue_res_33.out -457f14943f93096a56efb1e39f518721 emissiontrue_res_34.out -29c0e84c4147ebbbe13dff47a0be8216 emissiontrue_res_35.out -d060828ded63a9c5a50c9327155e11aa emissiontrue_res_36.out -6c06f1614e88d76ce4446844783ae7fd emissiontrue_res_37.out -d01806bee2f9ea43e38791067204bbb7 emissiontrue_res_38.out -84edb27ccedf10f5070fdd0fa84715ab emissiontrue_res_39.out -5bd836aa2bde75958ca4f0ce8289b38c emissiontrue_res_40.out -97cc7a5f88c1da8bff7d9e0b330d77d2 emissiontrue_res_41.out -9b8a1e9e6be1039cb1f1622463660388 emissiontrue_res_42.out -b6b24cc35c0100ce58b7a82100c2459a emissiontrue_res_43.out -b4679ffe8407fc62c0a22dd8c7c428d0 emissiontrue_res_44.out -43d1b94502a88ff523730180cddd34af emissiontrue_res_45.out -bc9aa74a8002bb5077f229408f943be2 emissiontrue_res_46.out -d062cdd6a468a79decaedf02145a309e emissiontrue_res_47.out -de044cbea16c1b76be2a89da204d291e emissiontrue_res_48.out -a8e54dc4fd8284e83b680ab64c673b6a emissiontrue_res_49.out -0fd9d6260db8ea44f04590a77a5085e1 emissiontrue_res_50.out -15dca814fdfe5371b11e96e6620ee2bb emissiontrue_res_51.out -542d8b6b68cf0f29d6875f9469b9c0c2 emissiontrue_res_52.out -ba0dc2f199e3249611e68f51d0b6fe69 emissiontrue_res_53.out -d678f1d4db97b3557abf775334f40951 emissiontrue_res_54.out -a0ea8ad72d0d5ed9f9e8db3c48005c8a emissiontrue_res_55.out -61a58005c067d135b0f3ab220e89bc45 emissiontrue_res_56.out -0e55dfd4f912e75f6aa0e9e78a8e7f16 emissiontrue_res_57.out -babe31bee94f23f5f045c69789a73fa4 emissiontrue_res_58.out -86eebccdad524ff8953303690c3d5fb8 emissiontrue_res_59.out -bbc58ea843f546b23cf12f6a85a1fc0c emissiontrue_res_60.out -dd7a72ce575ba2b4c5220d25e58986d6 emissiontrue_res_61.out -cc501bedf5c883ed02e2848e8cca5210 emissiontrue_res_62.out -b24a8d1d3b7274f9585795f42a1397b9 emissiontrue_res_63.out -b23162aef0a4fb491b84def9a0feae4f emissiontrue_res_64.out -8c47c979e0eeb82b33881d2032200186 emissiontrue_res_65.out -6b1528186ef94e5447ba40973f3b5c4a emissiontrue_res_66.out -174433f574a9c2e49e34139ff5b822c4 emissiontrue_res_67.out -c3b6c402c27b2a7e9059662170411399 emissiontrue_res_68.out -a8c2f9ec5208bb1f2a19ad2975439686 emissiontrue_res_69.out -9ce9c9ba6666897e6913dafab22e0d8e emissiontrue_res_70.out -9a76483560cf9340b90243cde6aadcc1 emissiontrue_res_71.out -ef75f604d9860a4d0cc5f0761b8f9801 emissiontrue_res_72.out -a995e52fc2f9a39f64816c623910754b emissiontrue_res_73.out -3a08a9e9adf5fd893d79a9b387b5d3cc emissiontrue_res_74.out -6a834b1319f6a5014bf58d9f9b149154 emissiontrue_res_75.out -8006b1851ac4c2319df1a98683e1d72c emissiontrue_res_76.out -56826fe2e354846f88dff9351fc532c9 emissiontrue_res_77.out -e8668133e77479672a3a725cca57082f emissiontrue_res_78.out -06e427a7717deba8c498eb9529c4cfaf emissiontrue_res_79.out -eb79233cdcb472b8150c1aa1e86a0c0a emissiontrue_res_80.out -3ca076477c940dd44c4edc4a048992a1 emissiontrue_res_81.out -1128ee095ea3ba5abda8bb971e411428 emissiontrue_res_82.out -aab99902bc3fde17c0665c22029cb862 emissiontrue_res_83.out -aa1ef95692dd65dbb60d4cddcbba60f0 emissiontrue_res_84.out -25eb890d194c5a2965c7d31eae9f757b emissiontrue_res_85.out -8e763c8d68e56d50fc03d42678d0d6fd emissiontrue_res_86.out -d87bdfcd95d1935d83271dc9939d6997 emissiontrue_res_87.out -295be7ed90936b5dab0c836e06821a17 emissiontrue_res_88.out -ea4ea131cf3400591ed86c132325fc1f emissiontrue_res_89.out -e039eca441abaf57c1b0ed2f84804105 emissiontrue_res_90.out -9293e4140f09ad97e531bdd52b2aebbd emissiontrue_res_91.out -13d4f2ab781ee11e0a992b1028fa944d emissiontrue_res_92.out -6ada63c560d2b0a085f3a046aef03dcc emissiontrue_res_93.out -8420642870003e04774f1f8e2fac120b emissiontrue_res_94.out -c34dd88378faad679c7e3829fbb7799c emissiontrue_res_95.out -4484ef9f9034d789571798f6d034936d emissiontrue_res_96.out -e1d0bd6d218abab79440017004b121e5 emissiontrue_res_97.out -f44d6fc277c613d20bb84b0b316cd72c emissiontrue_res_98.out -331dc7a09d84248fb6647a4498192310 emissiontrue_res_99.out -4d50cf5a9c2a201c907971bd2731428a gamma_light_curve.out -eeafb42ed10c039fac8cd75fbf5debc4 gamma_spec.out +f650198b01b47569ab168ee36d86f46b deposition.out +0eba26ed1bed53bf1c383166b2ce8fe6 emission.out +988f72cb38ec27263a24d7193bb030cf emission_res_00.out +f50119e7867996e6923c7466c35c6720 emission_res_01.out +9ecd8bb67539fc133fb9122b4800f9eb emission_res_02.out +345e93f2aff1769fb2bf86225f034b55 emission_res_03.out +d994ffe7a1dfc847ab65c17e0f0d7151 emission_res_04.out +2ab1322d640fbb327af4ca37ee4ea089 emission_res_05.out +632c78aab920b943204f9d22da2a7b20 emission_res_06.out +f4c1af136dbea6fae10cbd0ddd2b0b7a emission_res_07.out +05d625f7c954dc0773e7d7ef5fdfc3d6 emission_res_08.out +f4022cb358ed797eb6fcf15131ef9882 emission_res_09.out +48b703743425e5988d7b7ec4a92a0cc7 emission_res_10.out +bcd4265fad4e78e7c35d4712861ee39c emission_res_11.out +a6538176565ec6a418ced19fe02dcb51 emission_res_12.out +2c59a1f7801b8c07012554fb6739967f emission_res_13.out +e97efba101532052fd89014e088c894a emission_res_14.out +18ae4f7188347e63e3cc834cef3a325c emission_res_15.out +37abff16f6e683ff062acfd6e8bdce57 emission_res_16.out +835a558146a3081634bc486cef1ec5c2 emission_res_17.out +024aaadbd097aaaa6855624cd54fb037 emission_res_18.out +94a7b6e5de26ba1db30fb59b9bceaa0e emission_res_19.out +9a2c55e580f66d731bd488a92170688b emission_res_20.out +863e5ce859c58e973591d394bf1701dd emission_res_21.out +f365519cdbdc6cdc097784d95a511e89 emission_res_22.out +6fe5d6f73551573a6fd0080a9d8130a7 emission_res_23.out +e3ef7c3dba6197da27736ae38b471693 emission_res_24.out +4db914f99ca927de64f73fa2c4d1c61e emission_res_25.out +6da16d2d6e328fb840971b1cf8dcb821 emission_res_26.out +87a1f5d9f55765642d3b28b5309f200d emission_res_27.out +7404289f7493499e3040d32f059a3c96 emission_res_28.out +78e069f86bff097c336fa9d2a8458722 emission_res_29.out +e48496a17f0dbb4e348f5478ec34e7b1 emission_res_30.out +11ee8e798f4e25de065b03831314ebd9 emission_res_31.out +d7ee0c46ca6d29710ce09ae06826e6ef emission_res_32.out +38b92bbf20b825db38caf6f918a19817 emission_res_33.out +4af5525556984fd574192ba4e147a1a2 emission_res_34.out +ede1c3ad39e7c2f7bf6cfb3a7e46668e emission_res_35.out +563c5397165d54c9e9002e263a4378fd emission_res_36.out +b72973833fce02050c869dd913659648 emission_res_37.out +b313c4abfcb7ae86d272d6c6c026e6c2 emission_res_38.out +94786e4d8cba04c4d91025f2890993b2 emission_res_39.out +58d82be9cd5a0b9bd4c99eb437858062 emission_res_40.out +8ca93297c001849c1727b38fe93f1848 emission_res_41.out +36318044ed86e563423eddbfb6ca4d29 emission_res_42.out +5420ce9ae185e22bff0fda5f06ad6ace emission_res_43.out +b7b4a634f562d0b628422012d39e78a4 emission_res_44.out +25f30f5a71ce60718a54114d978aa645 emission_res_45.out +8857c56c28e0acd22ee214a3d24fa5d0 emission_res_46.out +de7d46f7211828699eeaa9178ab8519b emission_res_47.out +aa0307cbca4bfaaefbc28e93afafa078 emission_res_48.out +d8e50a1ffb19aa9e152c69ea847657d6 emission_res_49.out +c8815ef06cb7aad6f6a0f0dc1c104207 emission_res_50.out +9fed9dc1d5c315d9f4b4972f62107104 emission_res_51.out +a4b1e671d5313b4ad2f3cc3b1b844860 emission_res_52.out +c90b9d069fe69f2e3035a21382bbec29 emission_res_53.out +618f48e527121ac7c9d4c0f1b67b808f emission_res_54.out +b272ab52dca79d623235cd8d5ffdbad9 emission_res_55.out +ada9721b4ca2ae93d8fcd9c5d95850ac emission_res_56.out +8c2f86451add972b069356c20a808503 emission_res_57.out +e69e57d8cf94f7b52cd1314fda44866a emission_res_58.out +a6a86ddea1e46a02ac4f13f834d979b0 emission_res_59.out +64a12b69d4a8ae2a79666946300464ee emission_res_60.out +93b8003d856a816027eb0bae30401b59 emission_res_61.out +f2c9e35367e5cd50f693f68f32296b18 emission_res_62.out +502dc0c8128308b2509c0e23dd7b8ee7 emission_res_63.out +de78383d3c17447dc0399456eac6840f emission_res_64.out +39c8da259cff50fab6a2e82df34ca883 emission_res_65.out +8fba8aa3d934b06b77707e3ec2d9df6f emission_res_66.out +c6f8061fd57984f35921dbc6c6ae79e8 emission_res_67.out +b90cc837e238be46e9ce15f186c5ee90 emission_res_68.out +fcf71fae62c3579dd750971e647ba7e0 emission_res_69.out +c933cd99e4d294772a6ab274446e08e1 emission_res_70.out +21be865f268dedf55e6431ae54bf3434 emission_res_71.out +eaeba0d06e40b260e3ead5bc2c9bfd5e emission_res_72.out +01f43bea4a0407e674ffac6636b8728c emission_res_73.out +d7af6f11b2f91ab9a6d65e486aabfecc emission_res_74.out +876362688ab858051283d5b55090b85a emission_res_75.out +ba2878ccffe2053c6791cd140dfebc57 emission_res_76.out +bebada7dd0ce54535d4e9cfa97e701b5 emission_res_77.out +98e1a8fdd8e94ba56479cdea05b8c3c2 emission_res_78.out +31d20da7f1e3ccb197fd37c49834d85c emission_res_79.out +7acf09f79e12ef19eae1b506f841ee3d emission_res_80.out +032b5b24fa6c0e6ca881d288c1cac12e emission_res_81.out +fb11abed8a3f3425b41f77309f13bb31 emission_res_82.out +c58ad26d31832e42e5a554bac5dea629 emission_res_83.out +6fe698d573199413dd3e292c5b5d6fcc emission_res_84.out +6aa1eceac0903582af3381a069a70813 emission_res_85.out +acdaca1e6da526dc27d4c4a2c7f10b69 emission_res_86.out +227c2432b26bc91a9d6e07c9abffc0dc emission_res_87.out +522f7e62ff8bf673ea440790c05d5444 emission_res_88.out +e52a48ce77af3a373328336cb0044fb0 emission_res_89.out +27b2520ca712e56ee7482493833f88ef emission_res_90.out +c02111ece8a69014b2da5bb98fdec9dc emission_res_91.out +e3d224a3bb6ab56c5cff9cdc3d455f11 emission_res_92.out +96d2658f961b574e7909f28c00f922b8 emission_res_93.out +95578406173f79c33044d220013058ba emission_res_94.out +1b36e3f0f0ec86fc9d3b6e92f0f3c7a4 emission_res_95.out +ff0e8b342fd922d56aa27e627e95143c emission_res_96.out +23c10d8a41b5d8279d3461b56ee75684 emission_res_97.out +92b8ae474362eee62d63261174a0970a emission_res_98.out +e9a47e7d32767995bc9bce1b188ae26a emission_res_99.out +2526d5aa0c3a1899e68a12201ad8f311 emissionpol.out +a646d6947bf49889394893e07ffb9a08 emissionpol_res_00.out +3aaf74cf42b9e03eee5a43f41b50b6ee emissionpol_res_01.out +251e494153987dd20e59b757d6b1c016 emissionpol_res_02.out +98231392bf8762cd66c5fb389943cfa0 emissionpol_res_03.out +6ee49f3caf21014d8d5f622e0fa69a19 emissionpol_res_04.out +ea49d0699e415758a060456ae5fe023a emissionpol_res_05.out +f26665f540df879f79763038c508e454 emissionpol_res_06.out +829773210c9c9c47f45b62a0b32ab8b9 emissionpol_res_07.out +00f8ded7885f372332ad8dca685a0290 emissionpol_res_08.out +726c60ab27d68278d12e210b4dbea611 emissionpol_res_09.out +1be9185a5e8ecaeff0a7c4cbf243ef29 emissionpol_res_10.out +9f9d586249ce84cc4913a4ed720d0fe3 emissionpol_res_11.out +799e049c1a632593482658be47b19981 emissionpol_res_12.out +3ba5b171b0205d07224f942498b22f0a emissionpol_res_13.out +57b332a16497c45949b7d5f764275c14 emissionpol_res_14.out +dad9f1f4dbaa04875de8d0a2590da1e8 emissionpol_res_15.out +4fdc3fb59027aa62f28d486c8b4dba19 emissionpol_res_16.out +cfac4a065ef07c61bf9c2fa984fd9103 emissionpol_res_17.out +80609df2e1f70bf347d803b81dc79ff2 emissionpol_res_18.out +8ae9a7cf20a46331b38889f1720d9a61 emissionpol_res_19.out +0b1384a4804c13c4e451d1a180a78c4d emissionpol_res_20.out +e0be50682eb631ea46c5de023538fad1 emissionpol_res_21.out +f01617395b7fb002ac86bb279cd12f98 emissionpol_res_22.out +b48059d90b51cb32f9bb356f5adca176 emissionpol_res_23.out +145c9d54026208f4da9214cfe3da6eb5 emissionpol_res_24.out +ce6ac7c0782e13d7fcb0da760dca5c75 emissionpol_res_25.out +83d80069cd849e6f9484591184e2f48f emissionpol_res_26.out +b23315c9bed3aecead3d78c9fb1bb058 emissionpol_res_27.out +5f17a9bc49773edbaa274700e9a5c7fe emissionpol_res_28.out +72622f15e0dd523630beeaf5ed33d849 emissionpol_res_29.out +01db239234bb671617b46d14d6cd755a emissionpol_res_30.out +93b0dbac30a88946f8873f81fe09ebba emissionpol_res_31.out +597359ae2a1a8de19be6a4b072d63c2f emissionpol_res_32.out +8d7a9ad93e93ec25a51d3497bf84c9f6 emissionpol_res_33.out +27aa8eb9e2c5676ee0da784f46a1d384 emissionpol_res_34.out +13071f908c71726d6c146b461bedd3b6 emissionpol_res_35.out +cee094f01298ef7156f5da51dac06068 emissionpol_res_36.out +8bd0576974783e938b1b9bb17ed11b0a emissionpol_res_37.out +fcc4fd1504e57f053194089dd6dac47c emissionpol_res_38.out +2ccd1d77dde71ee2448f1f6c36100cba emissionpol_res_39.out +6e140a7a00cc512b83a9799bfa61dbc1 emissionpol_res_40.out +1ec299702ebb60e0f87fb8e5468fae47 emissionpol_res_41.out +b29d45ee1c816332d70aa13baf0e54ec emissionpol_res_42.out +773f4936e4df844bc724199d3ba99085 emissionpol_res_43.out +94b04179a7d32d2f0b0f0071baa6d3e2 emissionpol_res_44.out +5fdbf3e858aa90cb41547a315763b7b1 emissionpol_res_45.out +4465d64baf57ccc4526763460ba7b8e5 emissionpol_res_46.out +01abc9320380a295250dc67c28ed091d emissionpol_res_47.out +47f5a54c5599018181c081a6502cfc8c emissionpol_res_48.out +6e3c9e77962a27c8ac844ebe72272ec6 emissionpol_res_49.out +5e5cd4398cc92a12f220e3e9d140643b emissionpol_res_50.out +18a5fd14b037a597487d23d575fb28c7 emissionpol_res_51.out +75280d6b6cdc910a9134aa7d535a985c emissionpol_res_52.out +161f497438a83e4a7cbcba7592c60f75 emissionpol_res_53.out +a507a4c88dcad920c2072a07e987506e emissionpol_res_54.out +c7f26655a17a8fd4445aa05814ad7d5b emissionpol_res_55.out +9063ce5fdc2739d294053fa553aa6161 emissionpol_res_56.out +737cafedf57fbe89d6b92ad01f720e19 emissionpol_res_57.out +51161ca8019b141ff372ec09ec34987e emissionpol_res_58.out +60ca10258f28e458cb9bda26b2917481 emissionpol_res_59.out +e769529c10d61c87170381d9d706bebd emissionpol_res_60.out +ebd5df12a203a55e928ba764719b2719 emissionpol_res_61.out +1d4c28bf7ce73a2ad5d6be59c2587d0b emissionpol_res_62.out +2a9b0b0fd741fb79d6a86ef4631734b6 emissionpol_res_63.out +d1a264dae1dd1e261f8363fa7906bd1e emissionpol_res_64.out +fefec12cc449acb63c4e631dacd85a68 emissionpol_res_65.out +8ad2bcaf00cd8b87ed28ccdc955cb0c8 emissionpol_res_66.out +7157f956fec93d8686e205ecda764107 emissionpol_res_67.out +a3f8c1be4db2f9d3fa938fd868f330aa emissionpol_res_68.out +47102e2026b5bb0686da3556a593c35b emissionpol_res_69.out +9f7bf5f525deb7f88ea3e00efb541091 emissionpol_res_70.out +9bdfc22211de203609d78cb41b0a9e26 emissionpol_res_71.out +6e42b8b80c3c89fd4c6fcde145cccc41 emissionpol_res_72.out +0faa250d87a2534fcb8e56e7eee50b16 emissionpol_res_73.out +012ab88bbd191232058b1e53e43f3eef emissionpol_res_74.out +7331530a299a85b80f63f41cac1a9e87 emissionpol_res_75.out +b1287ea2766c5f695b64e6f28544aec1 emissionpol_res_76.out +88bbe194484b1c1470a160ed69ed2594 emissionpol_res_77.out +4e450b82f5947c824ce0b5e6213ec9a8 emissionpol_res_78.out +c02d783f4a225abb351ba3a557fdf38a emissionpol_res_79.out +e156dcd77e07f060df38539702505f90 emissionpol_res_80.out +230a2ffda4aa451fe71c2dcca359c77e emissionpol_res_81.out +bb34ecf8a685761ebb6a89b4f286a868 emissionpol_res_82.out +dd9746edcc6e6479212704309fe67ca4 emissionpol_res_83.out +7c0cc67157e4924efa4dcbdc83c3dd57 emissionpol_res_84.out +189465ea81fd2f4ac750b6648c249211 emissionpol_res_85.out +60e67857a9061696031692916156e47b emissionpol_res_86.out +03ea8de68db82737e7f86e29897ff9e0 emissionpol_res_87.out +8e2c5428c9abb033feaadb8222e3b417 emissionpol_res_88.out +f8b9bea83113e44602e9fd0200e2cf7f emissionpol_res_89.out +5ae4a8fbcc65fa0b404f0033d24e686b emissionpol_res_90.out +5f08f30399df1390f593531f52391ace emissionpol_res_91.out +c8bdd34b1f72a035fb6af874a7a8a5af emissionpol_res_92.out +e92f5a88706cac4e04b90902b315841e emissionpol_res_93.out +c40b80b8e668c470cc7898c8b1e2d43f emissionpol_res_94.out +5054bd10e52808156d253f32a124a67e emissionpol_res_95.out +a4237144571a7c2085f890f593b06c34 emissionpol_res_96.out +fe1c5a42bc542bce5566be674dbe099d emissionpol_res_97.out +5ddde7c4ebfaea939cadb2787ab10009 emissionpol_res_98.out +81266788e141739778f1ff3c6346c0bb emissionpol_res_99.out +d22634307359422454d5e0e5e566e841 emissiontrue.out +792f8ea5007fe18b87b62accfab383c3 emissiontrue_res_00.out +546d6075da2e235b67b5ede44b8ec4ff emissiontrue_res_01.out +a120fc0e870b2035895c391c418d903c emissiontrue_res_02.out +970c05fe2c92f2d0fa846b02bbb8b0a3 emissiontrue_res_03.out +6ebce5225d6cf217c44bd17a403581bc emissiontrue_res_04.out +8833f3da6194fce72904b28c65475c22 emissiontrue_res_05.out +b66de0c5a83fd648f70d8c5e037168e8 emissiontrue_res_06.out +5743c106a154dce61dc5ca7cb0016315 emissiontrue_res_07.out +2996744be540bb9d8bc7b215b830ac33 emissiontrue_res_08.out +8338bee211175834ed31ef75668f7063 emissiontrue_res_09.out +14c30ab21f73bcd1ff81ff484c5df294 emissiontrue_res_10.out +0404aeca70b6a43ac89d25024863915d emissiontrue_res_11.out +1255b449d43985352fcb1d7a8de91566 emissiontrue_res_12.out +b6796538e227e1449b0045db140f8b6e emissiontrue_res_13.out +6de0347b4bafe8cb7bc3c0668d3dc6d3 emissiontrue_res_14.out +e11d24366f8d4e25fda98ed8299e76ca emissiontrue_res_15.out +f8a8c5d198c8e975aaffb19c51c83d1a emissiontrue_res_16.out +76026e1f7a0086d414549fcc80e02559 emissiontrue_res_17.out +7b2b1a79b366d2aec5fb70b9a2e31d40 emissiontrue_res_18.out +57a16470c83796045ddc0ec6cee2b48d emissiontrue_res_19.out +d49cace307d37419d6c1e6b6c60dcb0e emissiontrue_res_20.out +935b417f0ac571147cbb8b852dae6e3f emissiontrue_res_21.out +d1ba3f5996d8df7e27aea082633d38cd emissiontrue_res_22.out +db9f2853da60388d3df595d08117de79 emissiontrue_res_23.out +eb4115a0d53bbe6360039c9a861d365e emissiontrue_res_24.out +89f91fa4dfee7c6e55f5cd71b0441576 emissiontrue_res_25.out +1ee8c05b5c0897804e6a746ace419a64 emissiontrue_res_26.out +7b30a0736f6fa669cd5c258c621fecfc emissiontrue_res_27.out +eeb4494347be6155d5a0aaa414e9ad1a emissiontrue_res_28.out +6f9581951dadab12982d7711ba0cfb82 emissiontrue_res_29.out +3548b1bdfa0bcf3271ced792e65bb3ef emissiontrue_res_30.out +dd23b2fbda52fb78f5cf4c7e9d2e594e emissiontrue_res_31.out +f3efc872f78208a455232be65db080d5 emissiontrue_res_32.out +2d0095802f2fabfb1adebc0344fdd390 emissiontrue_res_33.out +0790212696107bda913d2c3f322afd8d emissiontrue_res_34.out +48a85ee80533689f4a4c3e7565f3b791 emissiontrue_res_35.out +fed23fc98afa36e018aad1e0dc6942d5 emissiontrue_res_36.out +ff1a3d372c9cdefba2f593fefe8e1f41 emissiontrue_res_37.out +1bd2bc6e7607dad8a8aa911b45017539 emissiontrue_res_38.out +71f7323060318d47f77fe4297cf34d44 emissiontrue_res_39.out +0e03b3937918852a4c5516c26aedc444 emissiontrue_res_40.out +d127ce956d0810f2850576c045db33c6 emissiontrue_res_41.out +66e19b49f298c980c91c68dcbab3f3ca emissiontrue_res_42.out +58db006324fcce2243a1acffb3e66b2e emissiontrue_res_43.out +1f7113f0507975739f491f2d67099453 emissiontrue_res_44.out +bae21c95588cf267ebda51888c703862 emissiontrue_res_45.out +b046efc922221c2a11f2000b3bf81eaa emissiontrue_res_46.out +3f633ae5d52a0179289c074f039624b1 emissiontrue_res_47.out +714c2dd9fd50462d9dd3fea1412d3dbc emissiontrue_res_48.out +0e3d25d79277196ecf91bf05c8db1a38 emissiontrue_res_49.out +75cc3d3a1cd2fb3150dfa5b29e69c175 emissiontrue_res_50.out +5fd47d19fc0efdeb4f60e79fd1b9890b emissiontrue_res_51.out +4a96e3eb555286c40eef6768302d7a90 emissiontrue_res_52.out +6133ff8b4a8addb1f4d2bc21e31621c5 emissiontrue_res_53.out +52b4efafacdd3d67927de32ccc9bc370 emissiontrue_res_54.out +13311b1c54678124a1e745a471491992 emissiontrue_res_55.out +b6465b7f3643351fdc0a70047b4e3b2f emissiontrue_res_56.out +c55efe3826f48e740c369377f485532a emissiontrue_res_57.out +3ceeb24751cc78ad26fb4180eafd3b92 emissiontrue_res_58.out +4f80743d5d4176b8f0966c82e189ea52 emissiontrue_res_59.out +084236bd3d9601e35d025ca39c6e8aa0 emissiontrue_res_60.out +6c9c8aeebc9d03245d348b744b61d122 emissiontrue_res_61.out +06fbbaf07a8aca41d5a4a604ad75e7f8 emissiontrue_res_62.out +aa0ab6da1c813e0908b9ea7286c5636a emissiontrue_res_63.out +d943c95b730cee26d0f9e54426c3c778 emissiontrue_res_64.out +a1398bff72e00b9c8afef33944cccbd8 emissiontrue_res_65.out +f2a2a9402e83da9f090d1cd5e3c9a553 emissiontrue_res_66.out +73a15a467fb63c5ea152375e8cb10192 emissiontrue_res_67.out +5251598256eac4fd303b2ca1d4f7f780 emissiontrue_res_68.out +05e1c65667899db0d80fdca164d2ebab emissiontrue_res_69.out +9d3271f2e702c1159dcb591741663e30 emissiontrue_res_70.out +92cdb8aadad49dd873f1c13d332506b4 emissiontrue_res_71.out +6b8884aeed6e0cb33fdcfe7535080d7f emissiontrue_res_72.out +a3b733badf38493263dc26c34bb989ff emissiontrue_res_73.out +4e881ce09a498af4492f8b1bf3ca6237 emissiontrue_res_74.out +cae05ce9ecca843d1e197fbf03e631c4 emissiontrue_res_75.out +13680258b54b91645a65eab828569510 emissiontrue_res_76.out +2d00ccfe8950635ead1b3a67763a3291 emissiontrue_res_77.out +7fe4b1ac281cc74715b7a8cd1e157ac5 emissiontrue_res_78.out +4d0ecd7c78b095ed5d9ca96b36701b4d emissiontrue_res_79.out +9ea1a86f53d9a6f26a3c977a50152c12 emissiontrue_res_80.out +3560315ea522dc39eb210244afacb531 emissiontrue_res_81.out +cdbdd5be434042094c9eb3c4dc7bbaa6 emissiontrue_res_82.out +ff65e8993590aa82ec0ef26f387e0d20 emissiontrue_res_83.out +f736852c1dd2a2245347f4988db0ff7c emissiontrue_res_84.out +e8beafeced043561f954389b8bd1de4d emissiontrue_res_85.out +bb2bc2b36a5d2f038c55a066ea63f88f emissiontrue_res_86.out +b0ada059784dba5e858c4d112975c754 emissiontrue_res_87.out +89df46f26a44b37975ad74888cd76b06 emissiontrue_res_88.out +8e1a0cae69eeca946bb5978c1a82d641 emissiontrue_res_89.out +a59c48ad8f7389d137d0f189941b6b7b emissiontrue_res_90.out +47d86c414de92767b9fce5edb62bf10f emissiontrue_res_91.out +38a3a29844aa4d9b17c2e559352416ca emissiontrue_res_92.out +2300761171aa81a007f09ff9b99376fe emissiontrue_res_93.out +b0663dac106fd5f323ef385247f0565a emissiontrue_res_94.out +7da2a33bd6da961a7a5bb28b0bb08cb0 emissiontrue_res_95.out +423d64d49a27487cfd8544cc953743dc emissiontrue_res_96.out +aa52ea50c33db91f2109704d839be9b1 emissiontrue_res_97.out +5a79f21717cbcf03268ab031412b042f emissiontrue_res_98.out +3cfc4614cc4da027343c0d920040234a emissiontrue_res_99.out +a96a87d697ca41d4d02a25d44d22019a gamma_light_curve.out +278860838216f65b2df46ff7bf493ef3 gamma_spec.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out e08bbb965fac36412810b8769a13ab9d grid.out -0daa058efd056d5f72ebf54241d7c0e6 light_curve.out -eccc661f70e5884fcfd5ff50aa4a2032 light_curve_res.out +37ce27fcc45a6837ecc986ad038c112b light_curve.out +0691b11f3a8871fbe0b141ca8e551910 light_curve_res.out fb8c9b73ab7c5bb1fac556612d74812d linestat.out 07e400ff3495dc0b972ebe785db8b3c2 modelgridrankassignments.out -aca477827eff178e3cc7d69ef34e022e packets00_0000.out -ad403f7b1ce58262cab162d8c52e6899 packets00_0001.out -0a2517a98fe85dd57f6d4c6a0abc7320 spec.out -98ba9a554d8244a0c8bffd8c9d73c7d0 spec_res.out -127cddc13e2026653ec677bddaad858a specpol.out -17c348e654207dfdbbf0915c01db7be4 specpol_res.out +1ce914331cf5e604e7d2a6949d47f2bc packets00_0000.out +898ed91664f4c73c438cf05530c63994 packets00_0001.out +d88b1007563f469de4c3fa6485354950 spec.out +4389f39981b63d4db9c453fa8b8f5ebf spec_res.out +0463bd17f8c10622f2f13c1d4da2fb9f specpol.out +edffea9cc8b28c23286d9438a28f7924 specpol_res.out bc01f046eab9bf3802149e0ff63d5069 timesteps.out -fc30efcda41ed14972c013e74098102e job1/estimators_0000.out -1c8d82d3032d18549305c5637349306b job1/estimators_0001.out \ No newline at end of file +6ea207f7cffa562e62f9d2311376ed30 job1/estimators_0000.out +676fc28b1c1145b219cf3d7a93b2d589 job1/estimators_0001.out diff --git a/tests/classicmode_3d_inputfiles/results_md5_job0.txt b/tests/classicmode_3d_inputfiles/results_md5_job0.txt index 24942f464..20077b81c 100644 --- a/tests/classicmode_3d_inputfiles/results_md5_job0.txt +++ b/tests/classicmode_3d_inputfiles/results_md5_job0.txt @@ -1,17 +1,17 @@ -c524b815b9d27074f1981c43ac29693f absorption.out +3d2fdec1e8d7be5d284686950fa8919d absorption.out 6fffc8a4adefb2119fde9ccadbff8151 bflist.out -5f8cc880b9341445c00783bc8e1bddf5 deposition.out -01453920190b555bc7347234622cd611 emission.out -b8fd93be83fe661d22bbbf0f53dce42f emissiontrue.out -e586cd6ebc62208341b1fb1c37d324da gamma_light_curve.out +e77549779647478dae4195e0c7440c75 deposition.out +d267fc8e87ed6de0b6f02134959342db emission.out +9551c22a92df754a2c881a844156256c emissiontrue.out +2371265548b1da29928750800f62f4b7 gamma_light_curve.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out e08bbb965fac36412810b8769a13ab9d grid.out -086bd7e9f41ad4139bca7b8ba3954161 light_curve.out +065249d721a5b797eedb4dd8d564d34f light_curve.out fb8c9b73ab7c5bb1fac556612d74812d linestat.out 07e400ff3495dc0b972ebe785db8b3c2 modelgridrankassignments.out -04a6dac5e730e042af413f15bffdeabe packets00_0000.out -c318ca42d308319f959d3a0ed163127e packets00_0001.out -0f5340103bc6c36611dff85cc3058706 spec.out +19dd0f6482e8c2a8e5ad4c9c03633f5e packets00_0000.out +663b0393f95a92f6a0f0fd931957f5c9 packets00_0001.out +8aad0eabdebf722460b05185de9a1a8c spec.out bc01f046eab9bf3802149e0ff63d5069 timesteps.out -f88368939bc4312eaf65d44eef3eca46 job0/estimators_0000.out -d8aae79ab16f4ca2f64dc1c442b57cc3 job0/estimators_0001.out \ No newline at end of file +1a934a7e3ebea1474f6bf2c90fa8bb1e job0/estimators_0000.out +c30f3fc1c4e98443f219a528dfecc6c8 job0/estimators_0001.out diff --git a/tests/classicmode_inputfiles/results_md5_final.txt b/tests/classicmode_inputfiles/results_md5_final.txt deleted file mode 100644 index 5c078a97f..000000000 --- a/tests/classicmode_inputfiles/results_md5_final.txt +++ /dev/null @@ -1,23 +0,0 @@ -069938a09dfecd9cd8997f2bd925b181 absorption.out -aaf3f5018af7c2179f612f3d6c37d709 absorptionpol.out -c0604236aa7df99cda43e5c7bbe7b6f4 bflist.out -f0e6799258b54f35a38ab83a5fde2152 deposition.out -029bfae9cf2c93d180bc728324434003 emission.out -3723f2d7ef56499a02a21c61944acd32 emissionpol.out -30b4532c11eea8890bd118213441af0e emissiontrue.out -69c4e5cee569b5076d9e1d6f9cb7b796 gamma_light_curve.out -544d63ba01e9db28dc43311b3d738704 gamma_spec.out -057b226c371f3819cba5e04bfea3d114 gammalinelist.out -20bddb22b6f084a7abc105c210366bfd grid.out -930cecfe1ff351541acb69a6de613fed light_curve.out -5740f40190d45653e5fce57ddf577dc8 linestat.out -c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out -19d6928e153b769249392f76fbf628c1 packets00_0000.out -2698549a6b2240a98444e37052e8a436 packets00_0001.out -7ddb0915c7fedb723d61039cc29443b8 spec.out -5f1f14fd268e13a0e7bf9a61b5766bee specpol.out -040c71f981716a2abf0467a82b1ac13c timesteps.out -99bfc68bdb59019cbde8ce02858391b1 vspecpol_0-0.out -99bfc68bdb59019cbde8ce02858391b1 vspecpol_1-0.out -d5768a9bad50b8105447a94a08affcca job1/estimators_0000.out -1f92c6a323f21823dbfe76fa340c103a job1/estimators_0001.out \ No newline at end of file diff --git a/tests/classicmode_inputfiles/results_md5_job0.txt b/tests/classicmode_inputfiles/results_md5_job0.txt deleted file mode 100644 index 18b7b27a2..000000000 --- a/tests/classicmode_inputfiles/results_md5_job0.txt +++ /dev/null @@ -1,19 +0,0 @@ -f3b413171a447c8f6f4130e7d9e79094 absorption.out -c0604236aa7df99cda43e5c7bbe7b6f4 bflist.out -d64db10eddac9ea86dc988e1314bce7d deposition.out -bc3fa0983482e1bec6cc58f0a3c672be emission.out -f864869c9eefb25c79390c5b87278fb1 emissiontrue.out -7efccbea018d4330c778f3a6ac7485d1 gamma_light_curve.out -057b226c371f3819cba5e04bfea3d114 gammalinelist.out -20bddb22b6f084a7abc105c210366bfd grid.out -66a130b6340f9786fd7dde704184db94 light_curve.out -5740f40190d45653e5fce57ddf577dc8 linestat.out -c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out -32196c9763d29c3a46a50435d30f474c packets00_0000.out -43b382a8fd6f7180a5d5120e8a073311 packets00_0001.out -a10ad51bee9bafec0e8af851ab547252 spec.out -040c71f981716a2abf0467a82b1ac13c timesteps.out -99bfc68bdb59019cbde8ce02858391b1 vspecpol_0-0.out -99bfc68bdb59019cbde8ce02858391b1 vspecpol_1-0.out -2e37f4387a400276a61ba72530ae9e0e job0/estimators_0000.out -0134290c0abc1ec502b7d436f536e49a job0/estimators_0001.out \ No newline at end of file diff --git a/tests/classicmode_inputfiles/vpkt.txt b/tests/classicmode_inputfiles/vpkt.txt deleted file mode 100644 index 65d6c0eba..000000000 --- a/tests/classicmode_inputfiles/vpkt.txt +++ /dev/null @@ -1,14 +0,0 @@ -2 -1 0 -0 0 -0 12 0 -1 1 2 6 8 14 16 20 26 27 28 -1 10 30 -1 1 3500 10000 -0 100 -10 -1 -16.5 21.5 -2 3500 6000 6400 7200 - - - diff --git a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..f8bcbf0f0 --- /dev/null +++ b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,18 @@ +e1bbb9e147672e32bb50fba260343584 absorption.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +87aedf284bbaafae235976a623f1c1a5 deposition.out +7f7ee4926056b26206170ae330594d99 emission.out +4b1b8d917ab7f915b6f3b1afcef48011 emissiontrue.out +893ba3deb7c680605311de7f18ff01a2 gamma_light_curve.out +24ad8d7469380a561eecaedffde4e334 gamma_spec.out +4af36569cc2db94b41cdadbc03354ac4 gammalinelist.out +7db21c856b5d2784b14eabfd22e5b837 grid.out +9b198e6c609babbe223c63018acb7979 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out +33872793c9626adce4163a6f92f225bc packets00_0000.out +0b33390bb2afa6eb72e3eb66f8c6173c packets00_0001.out +5fbbcd0c30de4611d0f6e8e3b7644174 spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +6a6ebdc0648b2b21aef452bf8175f133 job1/estimators_0000.out +bc4574bd849763bb85d1c3811f193e5e job1/estimators_0001.out diff --git a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..c0e23510d --- /dev/null +++ b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,17 @@ +7e47b382ddacd759ae33771c8fba1bc2 absorption.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +6920ce785631e113d3bdec0e8bf42f66 deposition.out +43641ad17d13b4d07954d7fd9a45864a emission.out +d23d4ea71b94b1a4fb706f49a4c8c002 emissiontrue.out +b82ac74d554da3f567302ccbadff7366 gamma_light_curve.out +4af36569cc2db94b41cdadbc03354ac4 gammalinelist.out +7db21c856b5d2784b14eabfd22e5b837 grid.out +31be3b86c22c343df5b2f0f10c2edd0c light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out +d9dcb4378a7ed332f79a5071bae91a9e packets00_0000.out +b0d755d49c026bf4e67bba200191fd35 packets00_0001.out +8269008eb999358088439c0330a996fd spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +d791e9b02393ab142c5a3e616dd1b668 job0/estimators_0000.out +944041c0ef95bf74c720c433505efe86 job0/estimators_0001.out diff --git a/tests/kilonova_inputfiles/abundances.txt b/tests/kilonova_1d_3dgrid_inputfiles/abundances.txt similarity index 100% rename from tests/kilonova_inputfiles/abundances.txt rename to tests/kilonova_1d_3dgrid_inputfiles/abundances.txt diff --git a/tests/kilonova_inputfiles/compositiondata.txt b/tests/kilonova_1d_3dgrid_inputfiles/compositiondata.txt similarity index 100% rename from tests/kilonova_inputfiles/compositiondata.txt rename to tests/kilonova_1d_3dgrid_inputfiles/compositiondata.txt diff --git a/tests/kilonova_inputfiles/gridcontributions.txt.xz b/tests/kilonova_1d_3dgrid_inputfiles/gridcontributions.txt.xz similarity index 100% rename from tests/kilonova_inputfiles/gridcontributions.txt.xz rename to tests/kilonova_1d_3dgrid_inputfiles/gridcontributions.txt.xz diff --git a/tests/kilonova_inputfiles/input-newrun.txt b/tests/kilonova_1d_3dgrid_inputfiles/input-newrun.txt similarity index 92% rename from tests/kilonova_inputfiles/input-newrun.txt rename to tests/kilonova_1d_3dgrid_inputfiles/input-newrun.txt index 7fd8c94a2..4918076a4 100644 --- a/tests/kilonova_inputfiles/input-newrun.txt +++ b/tests/kilonova_1d_3dgrid_inputfiles/input-newrun.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -20 # globals::ntstep: number of timesteps -000 009 # itstep ftstep: number of start and end time step +20 # globals::ntimesteps: number of timesteps +000 009 # timestep_start timestep_finish: number of start and end time step 0.4 010 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/kilonova_inputfiles/input-resume.txt b/tests/kilonova_1d_3dgrid_inputfiles/input-resume.txt similarity index 92% rename from tests/kilonova_inputfiles/input-resume.txt rename to tests/kilonova_1d_3dgrid_inputfiles/input-resume.txt index f042db236..24e7330c8 100644 --- a/tests/kilonova_inputfiles/input-resume.txt +++ b/tests/kilonova_1d_3dgrid_inputfiles/input-resume.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -20 # globals::ntstep: number of timesteps -008 020 # itstep ftstep: number of start and end time step +20 # globals::ntimesteps: number of timesteps +008 020 # timestep_start timestep_finish: number of start and end time step 0.4 010 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/kilonova_inputfiles/model.txt.xz b/tests/kilonova_1d_3dgrid_inputfiles/model.txt.xz similarity index 100% rename from tests/kilonova_inputfiles/model.txt.xz rename to tests/kilonova_1d_3dgrid_inputfiles/model.txt.xz diff --git a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..f8d0fb41b --- /dev/null +++ b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,18 @@ +a28abc7f3400c76d3b38da6cc3815b13 absorption.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +0ff0e2fc9adf68667ac4bcff8f240d4c deposition.out +858da30ef4cfe71f53759dbc7dedd1ff emission.out +56c691bbc3c3e0d93a32e209c64bfc3d emissiontrue.out +a24dd7054c3bc0c35be4e74a5f9e6ed3 gamma_light_curve.out +756a288eb4e4d171fcb1162c0ec8c3d8 gamma_spec.out +4af36569cc2db94b41cdadbc03354ac4 gammalinelist.out +17cf657837d7df2969e3e1540b183cfe grid.out +a180e52d4ee94f2d679a1320ca628b06 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out +def7d4e67866038f5a075bc74d1d225b packets00_0000.out +a3bfac14bde7b6e25811144bfeacec4f packets00_0001.out +821414204b3fb0fab174b9895cbc6e68 spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +5d2348e48bb2d1ab37357fe5a90e0fcc job1/estimators_0000.out +dcf178e682d15f7bf16633e49bbf9bea job1/estimators_0001.out diff --git a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..4acf72fc3 --- /dev/null +++ b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,17 @@ +86efe2dfb75107386eb4a0a2b01825cb absorption.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +fe69c73227acf3be8a84a9c77554eea4 deposition.out +badaf4d5686bac45bcaf19efc6eacb02 emission.out +1cc31f4dc885f52aa180cef4b1dc3dc5 emissiontrue.out +28454eabb842b47ba951914a57add3eb gamma_light_curve.out +4af36569cc2db94b41cdadbc03354ac4 gammalinelist.out +17cf657837d7df2969e3e1540b183cfe grid.out +9626cbeee57ff23233a67786179520ae light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out +a84d3d4a9c7d1f61394f1b903d5bb244 packets00_0000.out +627178c8fb51ac34030cd547cc9d268d packets00_0001.out +6fe9dc86f38f3b31814186c336019313 spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +d77d94d656fc59fd894cefffc97aca2b job0/estimators_0000.out +6b0031f1c76b0152f43013f4defc7e10 job0/estimators_0001.out diff --git a/tests/kilonova_inputfiles/syn_dir.txt b/tests/kilonova_1d_3dgrid_inputfiles/syn_dir.txt similarity index 100% rename from tests/kilonova_inputfiles/syn_dir.txt rename to tests/kilonova_1d_3dgrid_inputfiles/syn_dir.txt diff --git a/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..0e9cef292 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,320 @@ +86a309ca9e8c8d7550f0157879510249 absorption.out +96919ce9810fac2b0dde28493f66786b absorption_res_00.out +22b381ff2e2f3d0bf5fc010ac3485470 absorption_res_01.out +f4bacea6bb489bed91e963fedb93a934 absorption_res_02.out +d235bf7e3c04ba0c86fdc6c4700fd60c absorption_res_03.out +3a7ec4ef4ab2d61f3f962a5d2e0d22e6 absorption_res_04.out +edbfd7a04d17fb4a8407dac1bac6f70a absorption_res_05.out +86b6fb4b0109ce56e9d8a1b4362f10ed absorption_res_06.out +a3f701f102b3108ac697da9d9e3a8dcc absorption_res_07.out +f8037e16edb908746ff773630bde63c0 absorption_res_08.out +ae615d40e80f0cd52216a3d534ae2992 absorption_res_09.out +09efdfa36442f72a327a0aa520f3de52 absorption_res_10.out +ac98d4364aa0c816e42306c12432e405 absorption_res_11.out +15d7fbe445503837e404d5aca87c0e5b absorption_res_12.out +e045b6c62fbaeaa2eec843279a6176f2 absorption_res_13.out +d1a3c09fb220cdc4f0d286f75811a4f8 absorption_res_14.out +51206607177af17670bd9ba08ccab729 absorption_res_15.out +c8897cfc8aec2e606bd44b3a5496f876 absorption_res_16.out +349b2eadfbd3017b4076f344ec532d83 absorption_res_17.out +4e065a36ea2c238d6326133a8ab5dd99 absorption_res_18.out +c9ec981ba1db9485e7eddb6e534e39cf absorption_res_19.out +ff4721e9ac1fa7d9f1f5e5bcc748bfeb absorption_res_20.out +5e4d721df0cd45677e28490e0952e21c absorption_res_21.out +4e61e8856163023a121ba3364fe899a7 absorption_res_22.out +e3fb216eec7c093bd60887236b04b0cf absorption_res_23.out +4de57786094488ecbf271c264a3a0cae absorption_res_24.out +5df39f1c10ed6bbb01cd79451320b6e3 absorption_res_25.out +6eb0bb63a5694435075070a4e04323eb absorption_res_26.out +cc421e29e02fcf28430b883118c3bac5 absorption_res_27.out +28e5184141266a447becfdc692969355 absorption_res_28.out +6d220612420d9dd0e0e8597fda16cc76 absorption_res_29.out +ae1a1b2e51ba43f890857ea39a62cc6f absorption_res_30.out +013872cd6659862f4e94684834e390aa absorption_res_31.out +f8054f64918dc629091873c8dcd412fb absorption_res_32.out +306abb0550492b74b725048b557db498 absorption_res_33.out +cd3b8735f2932d7fefd56225c8f3485e absorption_res_34.out +651895cbe121a17256bd2d811ab99f4b absorption_res_35.out +ca248c9c58576d405324a70f8061d8ed absorption_res_36.out +3cb2e279449358196a051e1b2af2bd6d absorption_res_37.out +c863a6795c58fbbffb2e62638d33e332 absorption_res_38.out +5f0ed9de13a657df45fc9ca6c392356d absorption_res_39.out +08c1f9ce81f77e24dd21b2d26c5532c0 absorption_res_40.out +3867ea1b27d523bec6340078954c054a absorption_res_41.out +47821386ad142d160f73a331258a3a7f absorption_res_42.out +9d54a224910c28b8a9790a29ee0d32b7 absorption_res_43.out +8a5f41fd362cbe2a5fe74ea21b97a4ed absorption_res_44.out +8e419f0d0f8bb85690061eff2d7a99f3 absorption_res_45.out +6094091433f4ef01280f4e674d102ab5 absorption_res_46.out +553a02e4b07881215c71ca9ae5df1bcf absorption_res_47.out +b665c6ccd4e1bfdc485c493c5a69d8e7 absorption_res_48.out +601a79c155fd99aa8f54717a35027ee0 absorption_res_49.out +e3855e1b30ad6a6839570b04b461e2fb absorption_res_50.out +edf4f26405eef6e983094cb4d0f28f9f absorption_res_51.out +e6cf8b29a7c0daf3d952c5335ca313e5 absorption_res_52.out +5abb58266b57568aaa262f1311e24ff1 absorption_res_53.out +126a67436744d573702fd79cbad03742 absorption_res_54.out +9ec726c9e9845fc4296b2197a8eb5eb4 absorption_res_55.out +260180c3dd22ad59024f4cbd71a7e10c absorption_res_56.out +3bf14bca1d14480dea13d2eafae326fd absorption_res_57.out +5cb01670b586b60317eff53bdce43a11 absorption_res_58.out +1aa40ffa5828c3d5518fed0a4844dad9 absorption_res_59.out +d5e0e5ea6c975a8d5841fc729afe6021 absorption_res_60.out +4f68e3bc450e5938ba4b4ba5f1283a5f absorption_res_61.out +8cf855afa17bc57cd1eb5011eb68a4b5 absorption_res_62.out +2bfe3521ad5ea7ae289a69f2afbacf05 absorption_res_63.out +79852db1116f87142f1d3cf60e43a9ea absorption_res_64.out +d2dfe4e3b15e7511bc82ee56ebb5063c absorption_res_65.out +8125b204f0ad8a1d1da8c5b4a23e554a absorption_res_66.out +ae76825555a38ea699ca66f3dac9a95d absorption_res_67.out +c2f5d37ef88e99ed3a9cae7b47930129 absorption_res_68.out +4ca8017dc272ba27b90ff74f75dd26d6 absorption_res_69.out +4647dc4beffbb57093b2745ce09cf804 absorption_res_70.out +8c56950777f72d57635ce8dbcbea880d absorption_res_71.out +f7c4c61e1909ca184279118e4725b53d absorption_res_72.out +e5651b5418753051a8c3add6c287e0c7 absorption_res_73.out +1ae8a64df9f21f9b0ef11d3afd98f52b absorption_res_74.out +5ca20c09371c4b4b88bd591e071cd489 absorption_res_75.out +1a03555b6a90a24a50585286e83d74da absorption_res_76.out +a37b4e8585e8dd8b83097b3beccec80b absorption_res_77.out +43730fdafb99f80dbf06fefdaedfdaaa absorption_res_78.out +f0ffd194857d86adbec59135aab904d0 absorption_res_79.out +89730d54a7184cde2375fa118993c3c3 absorption_res_80.out +8bc5217888ca0548c77318af2a68682d absorption_res_81.out +b9bd72bf798abca4189a8a3acf2c3cdb absorption_res_82.out +0671cdbd7b3dbcce8b21de58b78da2d0 absorption_res_83.out +a4f6004493ae4726cc6b6a18b41698d0 absorption_res_84.out +7fef4ccfa29a7d51f427cdfca67a0889 absorption_res_85.out +2dddad5738f05cc4c45b2915ed0f84b3 absorption_res_86.out +de94e8378e03baad1bf3486740d0881b absorption_res_87.out +b9260ddfe75c92ad369e9c6cec6310c9 absorption_res_88.out +0c48b13692a83061f45d3a1a93605bc6 absorption_res_89.out +d55d2c22a3f1761c848a38e5004bd8aa absorption_res_90.out +7d0bbc042dbabf3b93f2254402aefe5f absorption_res_91.out +b6750b24fc391795e031c3dbeb5d62e4 absorption_res_92.out +2a4f247325ecb8eb670d60bed18cc93c absorption_res_93.out +e5bdccfa7442c8ca766baf40d031e826 absorption_res_94.out +d4ead14a5547c95ae76056bb4e95c0d4 absorption_res_95.out +dce382446e007af2b1677befd0a7f9ea absorption_res_96.out +97e8addb338b5ec7b4767d22e075ed29 absorption_res_97.out +3f44b670ea956597b7bdf2c88aef05cf absorption_res_98.out +bda971ef712f64ae3b72422fe39015a7 absorption_res_99.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +f8ca9ed9a5caf8ae8998c83481372483 deposition.out +4d34b34173e2af0bc6977fd30f3d69a7 emission.out +a14f6a7fdc0f7455be5d0c944e94e007 emission_res_00.out +4b6dabd6f73562507c0b402e4ab25d6a emission_res_01.out +0f55630f2c73b8e22b7a3c9d1d616d9a emission_res_02.out +2061a1c3405a132eb05bd86ef76eecdc emission_res_03.out +085e5a078a4853291ab86e54250e49a7 emission_res_04.out +04aff0f30aa1a3d2a3acd32114272d69 emission_res_05.out +1180715998441158cfda9e787e3b4ae0 emission_res_06.out +47748e274a5c8cb9039a125b1dca7cbe emission_res_07.out +121d76da8acdb96efc9e84db78751905 emission_res_08.out +db8b61dc33665fe27628a35e230c5891 emission_res_09.out +54c45923b01110ee091832b0afebf400 emission_res_10.out +0b23cd17b2fef9b7f30ec621a664caea emission_res_11.out +40ba79579724845ebf4d6a36935d9432 emission_res_12.out +a0bfd8450c72a3863fbcb09b234f59b6 emission_res_13.out +8a7ad5de253716ac457c27b68b90ca0e emission_res_14.out +cef6efd72cdd743a1f50783d0df64cce emission_res_15.out +ea59e6fc6572e2762fca4f11357cee29 emission_res_16.out +1b21af8d31ce00e0451ff8254fbe419b emission_res_17.out +1d7fa97ce27b1a2ef1ba5e718f6d5303 emission_res_18.out +44925e969694bc6dc7ad7a1eb454acba emission_res_19.out +61443c798a707a9a2d4ac7cd4a5867c6 emission_res_20.out +7bdc8461d6047ff0e7c418ec0bafe9a1 emission_res_21.out +f22b6e06a247f06fe8e637c4defe80f5 emission_res_22.out +0599815780decfa6d4be0cfe12f85f54 emission_res_23.out +2270cae2bf5885b7dd2fffc272767d02 emission_res_24.out +bf7c69dd7ef649bf6045e2eb44596583 emission_res_25.out +ee8a7f8447bddbba5b54b15e72721514 emission_res_26.out +cc838c443c61d2a1dbb2cb7a3f70039b emission_res_27.out +ac7f364bc03b5ae55a2612884288519f emission_res_28.out +d679284cf19c2f9319a330d21e9e9c02 emission_res_29.out +63a90c59746247183922360d2ec2ca03 emission_res_30.out +ebaf9335b10990a1d59976438d434468 emission_res_31.out +b99db2296cf5a7d44ac17e777ad7d6e9 emission_res_32.out +92ce138de44a6a5aa0147256ba28f5b1 emission_res_33.out +40ca9ea517667ebfe828a803891ee702 emission_res_34.out +fab94072c3e6cdd733204cf03c3e942d emission_res_35.out +bba28ad75f42531d6656202cd6dd8fa3 emission_res_36.out +6c8be26a997c359d76c543e5c2b1e636 emission_res_37.out +f5ba6d6aa3a5312bdde1acf90080c9ca emission_res_38.out +439e2d58531d8d808a327c0b8e9377be emission_res_39.out +24d390c2802d79e0f1ee0513dcffc83f emission_res_40.out +5c7b74e71254b7941c81af95a57803cf emission_res_41.out +b7b59d27d06c3eb7cbc7dc45c9307cb6 emission_res_42.out +bcbfe78312281075cc19e7cc73dd6526 emission_res_43.out +24cfc3a625f8a750e1125fcdc52f6732 emission_res_44.out +309c3fb61e3d0486d6139e556d56a49a emission_res_45.out +68855018ef3cd36c3d54361859f3d60f emission_res_46.out +00d97b47ef5185c67e58667ee7c11397 emission_res_47.out +0e957a32d929eb96dcf9eb25b7cf40ee emission_res_48.out +9cbb2bef88e5c9c4f65c482485643fbd emission_res_49.out +d1230f0ffdd1e9dfd4a1cff961026e76 emission_res_50.out +6c219469985af735dda6280995eccd00 emission_res_51.out +08623bd829d7f5f0ec655fa010b7c957 emission_res_52.out +5fe95022445cd516d54ff0def4bfdfa1 emission_res_53.out +92c92b17a265d538a25bfac137099211 emission_res_54.out +b3188dcf3221fc3b62debf08a93ddef7 emission_res_55.out +cfff0b78ce32bcb384c104bd46e1c497 emission_res_56.out +59afaa4afbba03ee6ca9da2c2360141f emission_res_57.out +16a9831d7a0d33cca15807ac57967895 emission_res_58.out +e9772141601f69172170f9e230ff1466 emission_res_59.out +42836da4e3078cddb085d963893b796e emission_res_60.out +b6bb918a4f6ccfaaac4a967bbedb99be emission_res_61.out +92ea2c4ad0d98d901f0ee39abe32b371 emission_res_62.out +921605be21c6040ee49fd9865d301952 emission_res_63.out +2d1ba9b0de9d118aeab560bf1bbfa0fb emission_res_64.out +97b8db0bb0fce240b3c3ad7272120278 emission_res_65.out +e792cb53f9cd694dfc5d9ddd204a8646 emission_res_66.out +d7abefb41b338d5683ad2ad0a4c15d0b emission_res_67.out +e09a618c9d6c93e0aa432e60f6cc300b emission_res_68.out +e3cf19703db6699a4793900cb9e6cf4b emission_res_69.out +93dcb788e0738d293ba4b30dca28e786 emission_res_70.out +39ec6e4772a0a7c6cebffb26c1d10d5f emission_res_71.out +0e22e395a267a9075a47d0561e0bd7e4 emission_res_72.out +880b1908b73777fed72a998a6c048684 emission_res_73.out +4e0fcfb6a3e9c7192fa8bb477a11bbc4 emission_res_74.out +28b03b28d91efcd44d80519142709f0b emission_res_75.out +4fe0119e54e3ec898987f24e8a86f707 emission_res_76.out +7250957ddba6f286c8a71cbd402eb5bb emission_res_77.out +81c82aabb9e47d847bb4c3b9ab53023f emission_res_78.out +5876a33cde6290187b1c472b470cb14a emission_res_79.out +c9fe9ed6f96b1898c1554bd2375242e9 emission_res_80.out +55f9e21d18974016a36fbae0896ad3e5 emission_res_81.out +40dba19dc2f7d146b87b245b0bf5988b emission_res_82.out +e30f52f225c81d9e1c29b64428d06d49 emission_res_83.out +1a0781cd9ba61cf7e83a7eafe2462b6d emission_res_84.out +83ecf20fbd661159bec4138702a686d8 emission_res_85.out +7b14429d289b78c7d077068390d63f8f emission_res_86.out +65e4eda46de37e79015c1edac96611e1 emission_res_87.out +003ab4a54fb4de5cd0d9dcd70e249a37 emission_res_88.out +192e208d0cb51ebcf8d7d48eb9e1a76d emission_res_89.out +5356a14a6305788963b24789cc10e82f emission_res_90.out +7abb009aa52a6c5d4fef75fb9a297f27 emission_res_91.out +4929bd542dd5675e821385afa35e5837 emission_res_92.out +19a0108b505eb27497c2a39283651faf emission_res_93.out +975fc8a452adbe099d98df7ef016ac4e emission_res_94.out +23e8760ca9a5b3af979d3278572d021f emission_res_95.out +0518a92d33dfba8bc83fbe2886ba3951 emission_res_96.out +e1179985be4aae1c580b04b11c9afcfa emission_res_97.out +9b9e2dc6309f75c15ba9f0003cb5f089 emission_res_98.out +a07eb663c700bc1917a38a201f29bb64 emission_res_99.out +6ed758f6fbe331256584bb618a624820 emissiontrue.out +0fc9420a229d156f425fc02ab4df3393 emissiontrue_res_00.out +631995765c2c510f201b98ff7ca2ccc8 emissiontrue_res_01.out +01a8373f8b3ed8230643d5eb81945b92 emissiontrue_res_02.out +567f24e8f8d6444d487f0e8ba8f7dd02 emissiontrue_res_03.out +0d79c51219b24a903bce440c655e96a7 emissiontrue_res_04.out +43c03f02efc83f284d473ce1cfa54ea1 emissiontrue_res_05.out +c7226c53710dbc04a61efdcc82a77d29 emissiontrue_res_06.out +6acb1c14358831745e114671967c7399 emissiontrue_res_07.out +09d0fde719c14a09e34c50acc5d337e8 emissiontrue_res_08.out +f7a2e5f779a9b7b863f8c0803d0a944a emissiontrue_res_09.out +c9b64ccbfb4dcda5828c5c7138d9f2e5 emissiontrue_res_10.out +75c679fb178e4919295031b061c58db9 emissiontrue_res_11.out +f2912410b16d1baeb32036e4c22c18ad emissiontrue_res_12.out +a9635e1a162de0f7f1942f6726f53373 emissiontrue_res_13.out +788fa241258dc4a0a6455082b3ce90c1 emissiontrue_res_14.out +25a2b628b33527a7181eb7c2a7cd9851 emissiontrue_res_15.out +2323c0d674c18a1ea10937425214bbfc emissiontrue_res_16.out +88411722abc921c7d206969bd3edc6c7 emissiontrue_res_17.out +75d4773ba25f85dabc5b02fa7feb4880 emissiontrue_res_18.out +70d5ea4d03edcd1c841a2849981c38d3 emissiontrue_res_19.out +7d165a3e8476cbd47a619b43494b33b4 emissiontrue_res_20.out +558fb03379fe86fa86fd5964d8af7a77 emissiontrue_res_21.out +1f5ff42c571a846f7dbd4b5a4f34042f emissiontrue_res_22.out +91c1758697bf2937ebc63317936fe035 emissiontrue_res_23.out +736dbc093ea3312e7ceab32c611fbc28 emissiontrue_res_24.out +0177a4b2bf0cff947ce802d1e977d005 emissiontrue_res_25.out +3ecfa6926e5129e2c686ca84e0cfbc81 emissiontrue_res_26.out +7bf8ed3dc49db5b9d4a76386b9fd99fb emissiontrue_res_27.out +70b4ed0cc23ed2af13723e5caea724aa emissiontrue_res_28.out +25c7ef5d661284ca48bb41b6a2a69a63 emissiontrue_res_29.out +06a3fb2d91d984d84734137794c42f44 emissiontrue_res_30.out +86a0670664a50621cc956e424b97950a emissiontrue_res_31.out +117487afdefdf17a8a7551ac5976ae47 emissiontrue_res_32.out +1d5448e43eb87491e4ebfb54eced80a0 emissiontrue_res_33.out +4063562dc248fe77ee69f17096427ea6 emissiontrue_res_34.out +77c205e817b5e20a4f933f5882b5a6f0 emissiontrue_res_35.out +1734f4794ee5f549a1497c6d5a10b419 emissiontrue_res_36.out +84ac6925d36a581daaecefee81803113 emissiontrue_res_37.out +7bd68e60f7c4b27c37dc6de8c3101df6 emissiontrue_res_38.out +61b47309c41dae4b7294c26a84937d1c emissiontrue_res_39.out +936c79de77734132c36a95190cef39a5 emissiontrue_res_40.out +7b66d85f4d3d6d2e70aebb7c78c75a46 emissiontrue_res_41.out +39f4de2332c9a1646d57ae74a90402f1 emissiontrue_res_42.out +0c292420b15973ee6e43bf4c07557d0b emissiontrue_res_43.out +32b99254dbaef7149be95db076bc9b59 emissiontrue_res_44.out +3ebe71af8109d86d1fd3b51d406f772c emissiontrue_res_45.out +f4d5ed3e4a4c8eb660b9e8c70453cc0f emissiontrue_res_46.out +fa37ca3ced0dd1028c16d298b9ca3c39 emissiontrue_res_47.out +1270a176ad061d45e034466ef7d0d0e1 emissiontrue_res_48.out +801d865f8f5cb85446f81b9a8f668471 emissiontrue_res_49.out +1de729e08d0d04bb821189a5e4574e0f emissiontrue_res_50.out +5ab0f3401d0bf2289484fa93ba2e51bc emissiontrue_res_51.out +578a86b5b4b1082af2e6a399b219c2bf emissiontrue_res_52.out +ced6e2b40b6bc5dcbdf0fad825506cf4 emissiontrue_res_53.out +ca09e35d263592fa6fd94e23d19f9750 emissiontrue_res_54.out +f8c593c9b291fb3678b96b3e840d58bf emissiontrue_res_55.out +16a76959e29d9b5088484eaa554fd1b4 emissiontrue_res_56.out +393a824f334c03442904b60e6011df7f emissiontrue_res_57.out +87ae89678046e4d69b063d438bdef6a7 emissiontrue_res_58.out +c41fa2f80e71a90d4d59cdd9778babc9 emissiontrue_res_59.out +b045331d8d0d2535429f82e34d145100 emissiontrue_res_60.out +38fd36211500e98322f76663217c893c emissiontrue_res_61.out +e03e597cbd21846003ca2c5158b49cd5 emissiontrue_res_62.out +0712634195d7a5c64b786bf83f0dd864 emissiontrue_res_63.out +03c7c92105d90ecfc12176157e9c3228 emissiontrue_res_64.out +aa2887c62de648b9eb25a0fa21380ef6 emissiontrue_res_65.out +6951878df21e5682e2a10dd80303ff4f emissiontrue_res_66.out +25f19a03f988a3195410fb95777429d4 emissiontrue_res_67.out +f2720dd7fab4a9b908edc999ef3c1e58 emissiontrue_res_68.out +6336fd33314c78f7d1de5a0b2d83be30 emissiontrue_res_69.out +4e69c3e07be73d3c2bf782bf1ce85c48 emissiontrue_res_70.out +eab2f2eb43c4d0486e2dcc91142a6db2 emissiontrue_res_71.out +631c9cffa0ddbb2b55c0a425d0683109 emissiontrue_res_72.out +067aab91e49679a7fbb6b96fa2bda172 emissiontrue_res_73.out +82071418f1b7aba7815934d8ce379488 emissiontrue_res_74.out +ad8fb23b024931b99970e21b3f07d4fb emissiontrue_res_75.out +123190ff1553515a9a6572dd50536e54 emissiontrue_res_76.out +0e659baa137c2b273dd9bee55d49720d emissiontrue_res_77.out +307e0d4f8704f425762e28f92c9dabe7 emissiontrue_res_78.out +0484aa23ac88af69993e706d9b33f320 emissiontrue_res_79.out +04858401407bef0a009edb42be7dfd0a emissiontrue_res_80.out +086de8390270e7795fb7187d91f7e7bb emissiontrue_res_81.out +5ba2dd543a3175b3ad3ed049f2f5199f emissiontrue_res_82.out +e698ef10fbe48fb61d1c925ed862a371 emissiontrue_res_83.out +720f406966767d54c5a1c53f6b9ed13c emissiontrue_res_84.out +f532f26112e4e794eb345bc08158198c emissiontrue_res_85.out +7fefc72d23f83a6716ab46418d98b5a7 emissiontrue_res_86.out +fb41872c4eed81512d736b40391ea493 emissiontrue_res_87.out +cbf2f11164217e84f5ed3a94c8152ee8 emissiontrue_res_88.out +b839a3e36daaac426239ee3478c1e01c emissiontrue_res_89.out +0575ddfc84a09d051bc90cb67f4e289f emissiontrue_res_90.out +4df20b8eae53b779be07eae598ea5fa1 emissiontrue_res_91.out +75d4e59cb9a59a67fa2dd4bc0ad3bd0c emissiontrue_res_92.out +1855b09a3ff188eb0bc8e59e1311cebd emissiontrue_res_93.out +79a34ef2513a8c79ad879a4d76a939b8 emissiontrue_res_94.out +e206e98f8f8dfe959caecf570de928d5 emissiontrue_res_95.out +49ed91ae21377f6fe7ce1b9c1fb42fde emissiontrue_res_96.out +1eae0999c578acce094357c92b822c15 emissiontrue_res_97.out +2c5c8b7e9c91c9f7a5502841732bb00a emissiontrue_res_98.out +88e590f4cc18310826be892b5f5a9a8d emissiontrue_res_99.out +5f291ebeb25086a65078dcb92eec856e gamma_light_curve.out +0476f6c2a1e0023cf76bc16a0b087062 gamma_spec.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +589c74808ee61acc4d79bb4f84118992 light_curve.out +22c46f471ef3c2e126c8d4f37980f117 light_curve_res.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +a1c91f6d89797e1feaf21d4d6186545d packets00_0000.out +2a64c187ff3d5c4cda87068966083983 packets00_0001.out +6de6558232711b33cd042ab495c496ca spec.out +0dd442806e68ef09fb6b1d1f6b4a98ff spec_res.out +a351f1711fecd60c023d0ba7332092db timesteps.out +ade51ef62b573258ac4974bbea28f39f job1/estimators_0000.out +cead8ecae6e4fbeeb9f49b1824176379 job1/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..456097991 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,14 @@ +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +46149fb7fc6818ce05bbb5b4d6eefb34 deposition.out +7889f292c402838a73222e614531d1c1 gamma_light_curve.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +3db3e2413ea1e67b4bb119eed503ff84 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +8f36691a42745261ce309bbac0599a53 packets00_0000.out +bd4a751fc9d0dc1c7fe7f60865b41a71 packets00_0001.out +600fc00772d3c1ad16948b6332791a9a spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +5ce4fd7564baff28fdc53625e53a83b1 job0/estimators_0000.out +85a8cb83f6ae8bb6594abdfa7ccd060c job0/estimators_0001.out diff --git a/tests/kilonova_2d_3dgrid_inputfiles/abundances.txt.xz b/tests/kilonova_2d_3dgrid_inputfiles/abundances.txt.xz new file mode 100644 index 000000000..e4fa0e2fd Binary files /dev/null and b/tests/kilonova_2d_3dgrid_inputfiles/abundances.txt.xz differ diff --git a/tests/kilonova_2d_3dgrid_inputfiles/compositiondata.txt b/tests/kilonova_2d_3dgrid_inputfiles/compositiondata.txt new file mode 100644 index 000000000..abd9b1f0c --- /dev/null +++ b/tests/kilonova_2d_3dgrid_inputfiles/compositiondata.txt @@ -0,0 +1,13 @@ +10 +0 +0 + 2 0 0 0 1 0.0 4 +24 0 0 0 1 0.0 60 +26 5 1 5 500 0.0 55.8450 +27 3 2 4 500 0.0 58.9332 +28 4 2 5 500 0.0 58.6934 +38 0 0 0 1 0.0 95 +58 0 0 0 1 0.0 145 +60 0 0 0 1 0.0 150 +88 0 0 0 1 0.0 220 +92 0 0 0 1 0.0 230 \ No newline at end of file diff --git a/tests/kilonova_2d_3dgrid_inputfiles/gridcontributions.txt.xz b/tests/kilonova_2d_3dgrid_inputfiles/gridcontributions.txt.xz new file mode 100644 index 000000000..ac9f2b48f Binary files /dev/null and b/tests/kilonova_2d_3dgrid_inputfiles/gridcontributions.txt.xz differ diff --git a/tests/kilonova_2d_3dgrid_inputfiles/input-newrun.txt b/tests/kilonova_2d_3dgrid_inputfiles/input-newrun.txt new file mode 100644 index 000000000..e4b815b58 --- /dev/null +++ b/tests/kilonova_2d_3dgrid_inputfiles/input-newrun.txt @@ -0,0 +1,24 @@ +1281360349 # pre_zseed: specific random number seed if > 0 or random if negative +20 # globals::ntimesteps: number of timesteps +000 009 # timestep_start timestep_finish: number of start and end time step +0.4 010 # tmin_days tmax_days: start and end times [day] +1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] +80 # nsyn_time: number of times for synthesis +3. 0.037 # start and end times for synthesis +2 # model_type: number of dimensions (1, 2, or 3) +4 # compute r-light curve (1: no estimators, 2: thin cells, 3: thick cells, 4: gamma-ray heating) +1 # n_out_it: UNUSED number of iterations +1.0 # CLIGHT/CLIGHT: change speed of light by some factor +-1 # use grey opacity for gammas? +0 0 1 # syn_dir: x, y, and z components of unit vector (will be normalised after input or randomised if zero length) +4 # opacity_case: opacity choice +1.0e-10 # rho_crit_para: free parameter for calculation of rho_crit +-1 # UNUSED debug_packet: (>=0: activate debug output for packet id, <0: ignore) +0 # simulation_continued_from_saved: (0: start new simulation, 1: continue from gridsave and packets files) +1e-6 # UNUSED rfcut_angstroms: wavelength (in Angstroms) at which the parameterisation of the radiation field switches from the nebular approximation to LTE. +999 # num_lte_timesteps +0.0 5 # cell_is_optically_thick num_grey_timesteps +-1 # UNUSED max_bf_continua: (>0: max bound-free continua per ion, <0 unlimited) +2 # nprocs_exspec: extract spectra for n MPI tasks +1 # do_emission_res: Extract line-of-sight dependent information of last emission for spectrum_res (1: yes, 2: no) +0.001 1000 # kpktdiffusion_timescale n_kpktdiffusion_timesteps: kpkts diffuse x of a time step's length for the first y time steps diff --git a/tests/kilonova_2d_3dgrid_inputfiles/input-resume.txt b/tests/kilonova_2d_3dgrid_inputfiles/input-resume.txt new file mode 100644 index 000000000..8f4486546 --- /dev/null +++ b/tests/kilonova_2d_3dgrid_inputfiles/input-resume.txt @@ -0,0 +1,24 @@ +1281360349 # pre_zseed: specific random number seed if > 0 or random if negative +20 # globals::ntimesteps: number of timesteps +008 020 # timestep_start timestep_finish: number of start and end time step +0.4 010 # tmin_days tmax_days: start and end times [day] +1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] +80 # nsyn_time: number of times for synthesis +3. 0.037 # start and end times for synthesis +2 # model_type: number of dimensions (1, 2, or 3) +4 # compute r-light curve (1: no estimators, 2: thin cells, 3: thick cells, 4: gamma-ray heating) +1 # n_out_it: UNUSED number of iterations +1.0 # CLIGHT/CLIGHT: change speed of light by some factor +-1 # use grey opacity for gammas? +0 0 1 # syn_dir: x, y, and z components of unit vector (will be normalised after input or randomised if zero length) +4 # opacity_case: opacity choice +1.0e-10 # rho_crit_para: free parameter for calculation of rho_crit +-1 # UNUSED debug_packet: (>=0: activate debug output for packet id, <0: ignore) +1 # simulation_continued_from_saved: (0: start new simulation, 1: continue from gridsave and packets files) +1e-6 # UNUSED rfcut_angstroms: wavelength (in Angstroms) at which the parameterisation of the radiation field switches from the nebular approximation to LTE. +999 # num_lte_timesteps +0.0 5 # cell_is_optically_thick num_grey_timesteps +-1 # UNUSED max_bf_continua: (>0: max bound-free continua per ion, <0 unlimited) +2 # nprocs_exspec: extract spectra for n MPI tasks +1 # do_emission_res: Extract line-of-sight dependent information of last emission for spectrum_res (1: yes, 2: no) +0.001 1000 # kpktdiffusion_timescale n_kpktdiffusion_timesteps: kpkts diffuse x of a time step's length for the first y time steps diff --git a/tests/kilonova_2d_3dgrid_inputfiles/model.txt.xz b/tests/kilonova_2d_3dgrid_inputfiles/model.txt.xz new file mode 100644 index 000000000..0bf6e6026 Binary files /dev/null and b/tests/kilonova_2d_3dgrid_inputfiles/model.txt.xz differ diff --git a/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..dcc5c4250 --- /dev/null +++ b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,320 @@ +58e9480e053988088961a3515f58bab7 absorption.out +14639376503c2be22e164cf18a19c771 absorption_res_00.out +14f1290468979ef6f7c43115bbd61651 absorption_res_01.out +175160a10bed89fb0a0a7abe48442aa0 absorption_res_02.out +a1384044ae233b8ab92453adf5e0cf0a absorption_res_03.out +de6d190ec62b49e9fb3c68342a89a123 absorption_res_04.out +0bfa119d18b062dad3561fba074b656c absorption_res_05.out +634cb5d42fc23a68e889e7223bd20fff absorption_res_06.out +eeb6e1f0d7cf5bc76bc4ba759c041ab0 absorption_res_07.out +9ed91c5d8998c8a260133467ffe34f61 absorption_res_08.out +86ac8ee4f49b72f8ae5f0d286e0f898d absorption_res_09.out +6dd0d324ecb5f64fba5ab0b933d34862 absorption_res_10.out +7bcb36040d8928c0f705a9dad940d507 absorption_res_11.out +17976f6cb8896d73a540f5daffff9fef absorption_res_12.out +e1718f7a884902d78514c6c894808096 absorption_res_13.out +102121c27e797fb82f023868b6fd4a0b absorption_res_14.out +d0a6f4abca16da1fd7e54d014f44dce0 absorption_res_15.out +7972cd3c1f63bde17265014d043c37a3 absorption_res_16.out +c964e48ef99aad25003d0f4f560594bd absorption_res_17.out +6c09a398dca31588569aa4826984bf83 absorption_res_18.out +467b2ac792e5e33b7be1b6e7e3b20d9f absorption_res_19.out +1bb5f4a026abf31e7f981e7a6b88f95d absorption_res_20.out +918de21f9e3f28c1691f7ea7e20010e7 absorption_res_21.out +7d32b2c9603831fb97fe960cb6967a02 absorption_res_22.out +1c33fee201e59d9737c6d1a69c49ce30 absorption_res_23.out +8d7e2de6f87ae2411fdf3887c00c941c absorption_res_24.out +11cf40a11ab1a59b2da6395f25691e33 absorption_res_25.out +1f15bf1bd25c2a6699aef13d41de6189 absorption_res_26.out +966b8b4d011f72cf769dddbef3356932 absorption_res_27.out +66a8420f628d904f34b2129f433ff920 absorption_res_28.out +92d96250f433bf593a0b9fc682ed6d4c absorption_res_29.out +6def416a3c8a2807c9ab3c9c7d818ce2 absorption_res_30.out +11d996d26ef19c03a93cb4ed654506cd absorption_res_31.out +2061677c73785cc681af88634873111d absorption_res_32.out +774ed2382b2e34373e464e94f738f064 absorption_res_33.out +ecd13fe5ac5ce0be9bb1e500ad6e0063 absorption_res_34.out +912d1bea3c924cb3036b72e5de0765c7 absorption_res_35.out +0e45b17554ae270c601eeca1617ea16a absorption_res_36.out +7f3fdb1e9bb990d51c80e6a85fe31e88 absorption_res_37.out +980e73874740f298c8a5508dc384360e absorption_res_38.out +de5207d9a51f9967b4101c8e9105bc29 absorption_res_39.out +bcd9ba8b776b3e31e1408954cb17f529 absorption_res_40.out +ae37301d3a15a803152244ca196bc2a6 absorption_res_41.out +b70585a6c145eb351ca0f90b80fc655e absorption_res_42.out +4264ea4c3bd081420a74598446d49577 absorption_res_43.out +11352d19409f9dd09701fefa25b2bf18 absorption_res_44.out +9ab57ef0b10367b5c114f3098b76e549 absorption_res_45.out +da7f2a69c5730e71da15bf25e09c4256 absorption_res_46.out +e4d8e67f1588803d39b68c1b0ec2f8af absorption_res_47.out +977d302212393ef690ee2673e8144020 absorption_res_48.out +061f64ab5e84aa2682237da474b69a1e absorption_res_49.out +840d77835522fe5c57abd9e6ae0b9bef absorption_res_50.out +68a60fad9bc1e5976e374c70a74cf194 absorption_res_51.out +b60943a49414b1b6721ff95e22ba600e absorption_res_52.out +41c109af3f39b25a18b98fc768f75dd5 absorption_res_53.out +a1ee0695f98a4acb56bcfc20b133df6e absorption_res_54.out +0ae65d8e51db03b1b3e3c3c4967e02a1 absorption_res_55.out +4f22d69ad2c60b82a36178346de2e62b absorption_res_56.out +b0b708adad38dd14adff506ab9bc41fd absorption_res_57.out +8d468b8ab61a477bab4fe9393b522e31 absorption_res_58.out +572644fed92c10be4dd0586f1411c4b4 absorption_res_59.out +c1a9d28de9dc8529cdc93561cebde758 absorption_res_60.out +482d2aa99ad437c2a8b814e80556c223 absorption_res_61.out +36c68a6e7683afeb5692cd74cdfaf8ea absorption_res_62.out +4d6ab476ee1f016fa4cc3da93e95a4e4 absorption_res_63.out +fa50e595024976caef963dddf36ce1df absorption_res_64.out +08beca90a5be687c763a84989b28981c absorption_res_65.out +a9b21544e928bceed396f6b1c10650ec absorption_res_66.out +8ccfefa5c5531c32658fa5e1cb43da48 absorption_res_67.out +c8e4302a83631e3db686c751fd370406 absorption_res_68.out +21f7f6d61904139a5b74eb7fc2749653 absorption_res_69.out +51c946136cdc22e46db9f97aec34f4a9 absorption_res_70.out +c9ee01c8fecf28e78f29d37f4f39df06 absorption_res_71.out +9352e5e70a98c6414f57382b27647a83 absorption_res_72.out +c17ad04913547a1c03bd3bece161eeb9 absorption_res_73.out +6cf28a390172df5d71bf9c109de9265d absorption_res_74.out +d4402c1df468f35ca8428333c24f2ed7 absorption_res_75.out +5bc1d325ef44f204315925ed126dd71c absorption_res_76.out +ebc3fbdcc113c5fa579c5f404b9096a5 absorption_res_77.out +554cd8fc13404a10cea5ecb432918295 absorption_res_78.out +eb04cc66589af585cd8b3a48ed879703 absorption_res_79.out +b3c7247fe453b8479fd3a96074cd7fc5 absorption_res_80.out +f4255c8b3f82b670e0b8a75ad10195b7 absorption_res_81.out +19bee0943a4b80362d501c0932088eb4 absorption_res_82.out +1247ccba02f76e9f828b9e5758dcc4ae absorption_res_83.out +fb8efe22b502e12b1963c3172c1c8534 absorption_res_84.out +f3dbc89e8cdbe9e15ae64168368c7014 absorption_res_85.out +f0b6cc8477d83c40b88b70c3ea5958cf absorption_res_86.out +37086b3b6e3119b35bf5429e0abfef28 absorption_res_87.out +a8a1b153952d163fbc63b25fc30a6ce1 absorption_res_88.out +6fb2c2c67332c9c44ec7172a96c69e15 absorption_res_89.out +5aab4f7717c5b91ff004330b19719a98 absorption_res_90.out +feec8f38ba64438725b563b1128db3c4 absorption_res_91.out +1960ffdb11ff2272b2071296c1d10ccc absorption_res_92.out +12da524d35fd0587504c51c7efa76435 absorption_res_93.out +f9bdd89ecb781b9f6e6ceb5fbf1c0c99 absorption_res_94.out +422b2127a236811391d95246ae2807a9 absorption_res_95.out +4976c2033747eb367389e1de9dada10e absorption_res_96.out +870ef8fc41e47a3100b7c7109716ca16 absorption_res_97.out +57e456c0906dda812f6e416f389ccd40 absorption_res_98.out +86b3ba25e36c882fe42f8764ab07b6ce absorption_res_99.out +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +8366d3ebcd1ea3ce5e2416970a367ce2 deposition.out +bc6fa560e008247eadb6689f5c8b9c1e emission.out +fce82ffc9c5e49124c218996f5a77eab emission_res_00.out +ec34daf448fc654db1481b08a13494c7 emission_res_01.out +5e732c0da50f9c9b2ee9e41ccd48ac6b emission_res_02.out +d182d702dea57483d68e699db5c59c65 emission_res_03.out +90d5b329836b3be080352c6fb3083b4d emission_res_04.out +574879b597f762e080dbc9a80e258f45 emission_res_05.out +32ebbbf131f2fef209432abeedca5113 emission_res_06.out +0d174cd8c91c7c9bab50ecaf4241d9ee emission_res_07.out +9ba008e957aa06ab08b2e7a53697dc57 emission_res_08.out +e13f3e0a033d662d50e6f6902fd8bab4 emission_res_09.out +32174ddb8b9247239c333d2a5153bed6 emission_res_10.out +fbe6eb3c7ace73b7e2b3053730a26894 emission_res_11.out +82e596bbd6501552d69edbd1fbbf6750 emission_res_12.out +b6fb3fbc20f1b1870d39333b043c11da emission_res_13.out +23400db6ab0d6e29988eb9033c752e2d emission_res_14.out +44dfc8ef21ee931cdb7f95e801f17288 emission_res_15.out +55d55fc01142af8847e19c71136d4a4d emission_res_16.out +d3a67ebd68f8e64ceed320078ae422a1 emission_res_17.out +0378cc68f87c55d209344decfc5aa6b4 emission_res_18.out +c05b1c1984de7500d3369ccfe745f430 emission_res_19.out +f1d206123f62bc9fba0cc7507a901133 emission_res_20.out +012d301a2393c787e9d5f4ab351f6767 emission_res_21.out +01115f45d00f6dc5b4ffcdf8c186c710 emission_res_22.out +6cad767f0c093e17d3615a949e6b37d5 emission_res_23.out +88fb7eac7db84d31d52b291500ec1ba5 emission_res_24.out +3e4c14189fba2fa2a21c3b403e0c9f0b emission_res_25.out +bef4bc26b0e7bf0be8525cee6e37a31d emission_res_26.out +20f19442caf5a30bc879045c7fca4619 emission_res_27.out +b9ddd0ec8b6082e51725d39ca8942637 emission_res_28.out +20f26f345e42d1ca5917ed0a765bd3cf emission_res_29.out +a0837c3a520004e2b85ff75247b8f320 emission_res_30.out +96995dd125537f275fd7273de02a13cf emission_res_31.out +8fae5da59c37fb81c2b2ab44b812232b emission_res_32.out +635dede103e0e190098b45465e9f067a emission_res_33.out +bd4d91e8e35d03c9ff7ce0ea4c12625d emission_res_34.out +908b9a7abe4f28748c6338d777eaa90b emission_res_35.out +e99982dc2f7d9ff4fa4ca565df36633c emission_res_36.out +bc374e42d7753340308e08df01b7a4ca emission_res_37.out +cb1fdb302296c869d439d24dc32e9fa4 emission_res_38.out +26ba0611b928b54933ed083d6c778151 emission_res_39.out +475058f982e0daa61d0709d060072457 emission_res_40.out +49684a63f9a5e58832dadf0750bd2e98 emission_res_41.out +c6599f7ca75dc08465774c4266d8fd75 emission_res_42.out +4c20340eabfa378daf2ce6d2b3564987 emission_res_43.out +49f0d219aaa042e5dd23774f1e435e01 emission_res_44.out +5c6ac3326b4b35ac81a0ad31e6611aa6 emission_res_45.out +b95e31006f6a86e361e4f2284af7c222 emission_res_46.out +14530a215b50ae1aa7a84c9c33e4daae emission_res_47.out +c4c37333cb99bfcc55d1902c08e19c7d emission_res_48.out +e9db55cd89c5d654f2447623e4ea4cdb emission_res_49.out +83b3ba3ba96e7404b284132fc267d874 emission_res_50.out +9c9562548e00e1bd43cb33bc854c8c66 emission_res_51.out +eea48b65bffe125c5422e3da2274d152 emission_res_52.out +2b364e1456873a7a53af1e97d8ae11d4 emission_res_53.out +4f1250860e40d49ff159f43f6b0471d4 emission_res_54.out +81031b5e0fafdd258dbe3b889205b51d emission_res_55.out +dc546e956e69b08cee6054c481ac14a1 emission_res_56.out +8ffb00857894c98bf2b55664d9dc9a96 emission_res_57.out +3dd96627af15d3acede122ff3d4010a0 emission_res_58.out +49425c5b3a2281ecbe58b7c2fb35c787 emission_res_59.out +128bf7f8db28f0b0f971a5ea6d918a20 emission_res_60.out +7a10eae1478bcec225b2ca9d1559b3ec emission_res_61.out +2d994dec7dc39870cdd5dd53a6192f61 emission_res_62.out +5b280549f790396c5f3e1fdbfdd12977 emission_res_63.out +d8e3f56e1b3facfe5379160c48a40a3c emission_res_64.out +11f5817d0d1b622265df247f19e08769 emission_res_65.out +84838084f87173b083ac52f414b0e825 emission_res_66.out +efa30532f34528809a9b8dd2882b8620 emission_res_67.out +cffe4531f4359e198e43cd3ce4adca65 emission_res_68.out +943a13f7116b02b719e3b2747ec70f98 emission_res_69.out +df777e2fb91a2ef22d607c0e0a975b8f emission_res_70.out +b0417f26cf40add951aca352957f4abb emission_res_71.out +d398191a21a1cb5b5c337ba0186b7e4a emission_res_72.out +eaecd4b10bedda7e9a790d13d4ab5634 emission_res_73.out +99ce389ce21a68899ce6dc791159b4af emission_res_74.out +950697bc1f9d777f7cba9c25480f2489 emission_res_75.out +b9d0792f746bc961e395119d259b93f7 emission_res_76.out +3a4ba51004b4540a884833e3ee393538 emission_res_77.out +276afa5d59d0a5b01a398d2e0ea59f15 emission_res_78.out +b738d1a6f422be8801d484724a159f91 emission_res_79.out +ed8eb65b7e44b84279f3f91a2c7fd460 emission_res_80.out +9d6b3dcc35982fc18e6eaa0c30b0374f emission_res_81.out +bd056e202e0625909db64f41218ca12b emission_res_82.out +b0b2ed9a3f1aee340442e4bced8f2ef3 emission_res_83.out +ed498b9e8ac1a841853da32fe775e1db emission_res_84.out +cd2a878386c9652456250abab80acf1c emission_res_85.out +23e0223f08c621f5438bb7cbb0483152 emission_res_86.out +051707b966ea8130cb9f392906d7bea1 emission_res_87.out +7bee63a0cb4f123224ec5abde90d42b4 emission_res_88.out +4542f32d07cf213726c26c19ee31c3dc emission_res_89.out +0301fdb39c96a8bd664b2f5647e2da85 emission_res_90.out +797985243f66da45812a85511bfd9f06 emission_res_91.out +6986946cf99e36026d3d157779e3e19a emission_res_92.out +ee26af93e3e7ca075927fb34134403fb emission_res_93.out +3ea932e91d60dbf2d278569a17148978 emission_res_94.out +b0c753216babe27b7b054296742ed135 emission_res_95.out +00129c83b8a5202b35f19c38ff5ddc45 emission_res_96.out +2c9422c76769c391010029dba3b465f5 emission_res_97.out +e869f8abadbd5e7b105ed70337ebb254 emission_res_98.out +08b5ec8e2503540a224065d615878102 emission_res_99.out +9c52452f8f3274a371265eefd9adc0ad emissiontrue.out +266ad84582cfe1925318cb59413a6220 emissiontrue_res_00.out +9d89f0586c7aef47c5bac932c5e097d0 emissiontrue_res_01.out +168be5dfefd0779c50ffb8f955d6a340 emissiontrue_res_02.out +dd3a9eb027a89cafdb78664e0333e37a emissiontrue_res_03.out +0dfde129876b447f0491184d482c1ab5 emissiontrue_res_04.out +d9c00947f33207153abbfd5132597ba6 emissiontrue_res_05.out +4c16b6b16760c157384aa58eec429704 emissiontrue_res_06.out +1523304ff88a8e06bdf621c0f02be023 emissiontrue_res_07.out +d114966820b6338df3a6c0114f4125ae emissiontrue_res_08.out +0a2fe2aa0366532296e83e9cd59cf01d emissiontrue_res_09.out +e28d09960c53b3190caf5f6b8d33f0e6 emissiontrue_res_10.out +30cf111f1421f4493f9801d190618f50 emissiontrue_res_11.out +9350f706d70258f3c6edd6863bb22376 emissiontrue_res_12.out +c77e3b53d4a49fe421bcefad19cf3943 emissiontrue_res_13.out +20ad1e7156facc46e284c6d5910b3119 emissiontrue_res_14.out +5a9ac54ddf5c12139024b9167e73cd0c emissiontrue_res_15.out +75bd573f42b0ef75d721fb886920d9ba emissiontrue_res_16.out +80adec711a1b6da985aaa23785182ad3 emissiontrue_res_17.out +e0434a2095b946673f0981c4c4f974a5 emissiontrue_res_18.out +72c4c0670f5c9b3ea9282b0ef525445a emissiontrue_res_19.out +7c1cc7943e94f3d37ae9d69ad5fea95a emissiontrue_res_20.out +62f67171618aa3bcf3157a733d9b6395 emissiontrue_res_21.out +f7a4e0b7f9fb79e25915acec63708cf6 emissiontrue_res_22.out +56e445d7a12090ba631fea751480c856 emissiontrue_res_23.out +1d14b52c618398250ab168bb9629609a emissiontrue_res_24.out +68335aba2af0de9166c7097ea4991b00 emissiontrue_res_25.out +eea5f1c7c7c8287909c586f24f3c3747 emissiontrue_res_26.out +3343494ed80c50e98a2163e93f70bdbf emissiontrue_res_27.out +bad265db35e75a14f90604951e881e49 emissiontrue_res_28.out +231345a1e4a007a6e0d3fab0f58b13cf emissiontrue_res_29.out +25d5076a933b79db79c0a4d2f30c7c7d emissiontrue_res_30.out +e11f598dc4c08aaa2998d8d7cbfd8272 emissiontrue_res_31.out +5626a9f30c209dbd8066435653baec63 emissiontrue_res_32.out +e8695c0214de1b7fe5945f196a0dedf9 emissiontrue_res_33.out +60e9aafe6b059450ce249d06a6497c6c emissiontrue_res_34.out +e09111ce9b76a603d371c94f4631d254 emissiontrue_res_35.out +af61ef53bf8316966a85b18514e1f41f emissiontrue_res_36.out +16d871a20cb891853e5008c48acad453 emissiontrue_res_37.out +01e0839ead63e7a6f17566f5954f1829 emissiontrue_res_38.out +0d52bc4960b0ba8de365aab0411177d3 emissiontrue_res_39.out +27b5c7a2a32cacd119e025fa9f52082a emissiontrue_res_40.out +ca5dde8db09898915a41bfcc6462e547 emissiontrue_res_41.out +99ac492f50644d8b32296f061bd75ae3 emissiontrue_res_42.out +049647e9f546ba6a7ae6649d58879372 emissiontrue_res_43.out +7a0ed70c1790023de378a8e3486a4145 emissiontrue_res_44.out +a0b6325d94d76aa15e1e836273a86150 emissiontrue_res_45.out +c4850b369d5fc1976ca63e2516de1d43 emissiontrue_res_46.out +fcfe5e6912c560b1d0eecd32c3310e90 emissiontrue_res_47.out +d0b5c9524343965d5d4f3af1d2b64fd2 emissiontrue_res_48.out +779841fa9d3b26bc83bf31a2cfae5f2d emissiontrue_res_49.out +03628847b35041881f829f6e004b7ead emissiontrue_res_50.out +9e0593a3ba28197c27a237986d957a05 emissiontrue_res_51.out +38c15405b36252799d48bc138e08a230 emissiontrue_res_52.out +5d3e54ac3fe0650bafb2ff5632d0c663 emissiontrue_res_53.out +07e0bf3f11004edf87f55c9dc47a230e emissiontrue_res_54.out +c7d5b63c3a66e81d6f67d05863d5336f emissiontrue_res_55.out +a23645be33c15d81035981121cdf513b emissiontrue_res_56.out +2b45c13a79f1b80e2fe185181d0feb93 emissiontrue_res_57.out +bd1da9de6c7082e25f616bea0ddf46f0 emissiontrue_res_58.out +922761f9a847f526e9a90244bc78e46b emissiontrue_res_59.out +43172d21553f6e88ad6bd1a6bc214ecc emissiontrue_res_60.out +4b14bc1cec45c1394dcb394c1e078d60 emissiontrue_res_61.out +1e262d5a8f59dacd6dd515621990929a emissiontrue_res_62.out +181fde6d37b8eeba181566c897c35d34 emissiontrue_res_63.out +191aa3eb03f228842022d4d22c109ee3 emissiontrue_res_64.out +0267c0ab76c4dff67bea6694740660d9 emissiontrue_res_65.out +329d7b730e16ad79e7aacf8f8fac99b8 emissiontrue_res_66.out +4727ef5cf2e11b968b7d254e43e50c64 emissiontrue_res_67.out +b91a60348dd5a0ef9f4e4843aaba0434 emissiontrue_res_68.out +7d31cd0710b10f9785e7aa9de4301df6 emissiontrue_res_69.out +e8dbc210afa65726cd96d26821730967 emissiontrue_res_70.out +2cae2c119835fc8fffa0402c42235e0a emissiontrue_res_71.out +414186c03c417034882100a2790b0289 emissiontrue_res_72.out +7940f85ec2103ea45cbf1fbf471eea6a emissiontrue_res_73.out +77cb913678c1dab259b89ef3bb07887a emissiontrue_res_74.out +9fb3049258cd8b4d4874a0e2789e7228 emissiontrue_res_75.out +2b6bbd278a8ecc8620575c50ad20ad49 emissiontrue_res_76.out +2a83cdd578e175e7ddb35a3c3ad1dbd5 emissiontrue_res_77.out +7fa4420371c20124bd2881967cf605b5 emissiontrue_res_78.out +4dc95ffbbda3cbd2f0e575085d6f067a emissiontrue_res_79.out +1a0fb75927c4215fd79125942b44ea7f emissiontrue_res_80.out +e5f17788de93fb55276ce657f11c3610 emissiontrue_res_81.out +216df8b6f64a7c37f54d5c4eb23de731 emissiontrue_res_82.out +6eda66161b45594b058162949c0a2afc emissiontrue_res_83.out +4d992234d115afbea51ee377737bce44 emissiontrue_res_84.out +b155694cce857196c3c6a47e326788c0 emissiontrue_res_85.out +325fb0a55db0f827f5ead014adba486b emissiontrue_res_86.out +6e9d11cec3b050353b1f952172598547 emissiontrue_res_87.out +c20006e19b09a6933d2e797acdebd53b emissiontrue_res_88.out +bf4886fa13bfbac78e68ad1f0bb92043 emissiontrue_res_89.out +a29843212383b49fef863c0ee42707b8 emissiontrue_res_90.out +097fb55d9a082e2f1aebf4645eaba329 emissiontrue_res_91.out +d8065be9755e8705dd0a7d731d6ef066 emissiontrue_res_92.out +55bfb71845a1026247780d2cd821e6f4 emissiontrue_res_93.out +05c8d128047eb6d0440ea16af9bf7f3c emissiontrue_res_94.out +855759f98e997f5947047e9a8b600628 emissiontrue_res_95.out +98cce02a0253f396dfc1ca04e2a677ff emissiontrue_res_96.out +e80877705736b18c03533e8a89f5b5fb emissiontrue_res_97.out +8e24c8a8463b9dd48e00e720cc00357b emissiontrue_res_98.out +cb3ba58ba4beb0a1071b2f975fe47b2e emissiontrue_res_99.out +82a585a5ca9ffcaff2ba7eb780265741 gamma_light_curve.out +d22f0480629cf543df9cbc58dca6cef1 gamma_spec.out +2b769145664780d4f90b07f963588536 gammalinelist.out +b72b67aae10074c2b0915aaad7d9ccbc grid.out +0a19c2e25fca1ae65278653adbee7c0d light_curve.out +d59673879699412cfe041953e4daa1e4 light_curve_res.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +9e9ea5eb62a7f904d2d52ac2c797e307 packets00_0000.out +14bc4d02c97590b4c351604253126901 packets00_0001.out +f2bea0bcfa94e8edcc00ce440857aa26 spec.out +bd96aa33323c64e02bb6b901f304bfbb spec_res.out +a351f1711fecd60c023d0ba7332092db timesteps.out +ce0c00c2d37a2829bbc04f1860622864 job1/estimators_0000.out +a25884811cb9bcd68c7a342852de882c job1/estimators_0001.out diff --git a/tests/kilonova_2d_3dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..be6e9d755 --- /dev/null +++ b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,14 @@ +f9bb214eb7f1ac22791a13c8025c4887 bflist.out +a02f3e5c0c16eb848950fe775ee4caa8 deposition.out +ed035872d205ede8cbefb6f6e66db7fa gamma_light_curve.out +2b769145664780d4f90b07f963588536 gammalinelist.out +b72b67aae10074c2b0915aaad7d9ccbc grid.out +b2e8ee953967d6709099ad05be61b09f light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +b435eb715747a70d3372b1cc98250065 packets00_0000.out +cfb5460a9316c79c660bf7d913ea8e08 packets00_0001.out +f8cfd141dfa301309bd0fba60c6f0c71 spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +b66700748a8a5585562388ebca38f851 job0/estimators_0000.out +566cb793642c4e4ac43b50de2d114193 job0/estimators_0001.out diff --git a/tests/nebularonezone_inputfiles/syn_dir.txt b/tests/kilonova_2d_3dgrid_inputfiles/syn_dir.txt similarity index 100% rename from tests/nebularonezone_inputfiles/syn_dir.txt rename to tests/kilonova_2d_3dgrid_inputfiles/syn_dir.txt diff --git a/tests/kilonova_inputfiles/results_md5_final.txt b/tests/kilonova_inputfiles/results_md5_final.txt deleted file mode 100644 index 8160fd31b..000000000 --- a/tests/kilonova_inputfiles/results_md5_final.txt +++ /dev/null @@ -1,18 +0,0 @@ -95542d77876e6f397ff600dd92fbd595 absorption.out -f9bb214eb7f1ac22791a13c8025c4887 bflist.out -0485f42bbf37d9f7e6f9a3b9469c3b94 deposition.out -2183c62679cc04a8a31e894ba59ec5e0 emission.out -89321348b06a320ff64a7ad2a7e7e3a2 emissiontrue.out -6eca8b3e749ecb29800ee24b637e179f gamma_light_curve.out -c842a494a2c00fc36c5d800adc22199e gamma_spec.out -65cc464631353798bfeb1827f4affcf5 gammalinelist.out -17cf657837d7df2969e3e1540b183cfe grid.out -f2491b46c120da8f361a7e5eb3ea9247 light_curve.out -9773becefdfe4afffb041b703210c67c linestat.out -f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -f956be3397e7e8b09c8a5124b7173463 packets00_0000.out -78039e41502f02132134293eab5a1339 packets00_0001.out -7a0ec83225121a619e57d9651966a8b6 spec.out -a351f1711fecd60c023d0ba7332092db timesteps.out -401299c99599a19eb47494add55e7d68 job1/estimators_0000.out -44083ed192c1572a164a693626d7a56e job1/estimators_0001.out \ No newline at end of file diff --git a/tests/kilonova_inputfiles/results_md5_job0.txt b/tests/kilonova_inputfiles/results_md5_job0.txt deleted file mode 100644 index 1a57c0ba2..000000000 --- a/tests/kilonova_inputfiles/results_md5_job0.txt +++ /dev/null @@ -1,17 +0,0 @@ -3ea8821bd59d38690ec6442441ae9a0b absorption.out -f9bb214eb7f1ac22791a13c8025c4887 bflist.out -0cdcde572e0cb8f0f30f28878f990583 deposition.out -c42703eaa62aadd9fe30487c69425607 emission.out -cf929c867784306d6397ebea060ea12d emissiontrue.out -3b638b4a9e710452fdb3c1312adcc305 gamma_light_curve.out -65cc464631353798bfeb1827f4affcf5 gammalinelist.out -17cf657837d7df2969e3e1540b183cfe grid.out -d7c45301f40ee6ffe51e734764e7dcf8 light_curve.out -9773becefdfe4afffb041b703210c67c linestat.out -f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -90cf4df88ba47decdb9e565ef7257948 packets00_0000.out -ae9a146145bb688157baa32f9735006d packets00_0001.out -daf767256c8cf00f49048d9ebbb59cd1 spec.out -a351f1711fecd60c023d0ba7332092db timesteps.out -c8341395a154b5efff56ac137660726a job0/estimators_0000.out -e538777e662703ca2bf377c45bd3dc25 job0/estimators_0001.out \ No newline at end of file diff --git a/tests/nebularonezone_inputfiles/abundances.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/abundances.txt similarity index 100% rename from tests/nebularonezone_inputfiles/abundances.txt rename to tests/nebularonezone_1d_3dgrid_inputfiles/abundances.txt diff --git a/tests/nebularonezone_inputfiles/input-newrun.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/input-newrun.txt similarity index 92% rename from tests/nebularonezone_inputfiles/input-newrun.txt rename to tests/nebularonezone_1d_3dgrid_inputfiles/input-newrun.txt index 0d91da4ff..7840a6b33 100644 --- a/tests/nebularonezone_inputfiles/input-newrun.txt +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/input-newrun.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -10 # globals::ntstep: number of timesteps -000 008 # itstep ftstep: number of start and end time step +10 # globals::ntimesteps: number of timesteps +000 008 # timestep_start timestep_finish: number of start and end time step 170 230 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/nebularonezone_inputfiles/input-resume.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/input-resume.txt similarity index 92% rename from tests/nebularonezone_inputfiles/input-resume.txt rename to tests/nebularonezone_1d_3dgrid_inputfiles/input-resume.txt index 2f52dc68d..de564696d 100644 --- a/tests/nebularonezone_inputfiles/input-resume.txt +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/input-resume.txt @@ -1,6 +1,6 @@ 1281360349 # pre_zseed: specific random number seed if > 0 or random if negative -10 # globals::ntstep: number of timesteps -007 010 # itstep ftstep: number of start and end time step +10 # globals::ntimesteps: number of timesteps +007 010 # timestep_start timestep_finish: number of start and end time step 170 230 # tmin_days tmax_days: start and end times [day] 1.33 1.330000001 # nusyn_min_mev nusyn_max_mev: lowest and highest frequency to synthesise [MeV] 80 # nsyn_time: number of times for synthesis diff --git a/tests/nebularonezone_inputfiles/model.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/model.txt similarity index 100% rename from tests/nebularonezone_inputfiles/model.txt rename to tests/nebularonezone_1d_3dgrid_inputfiles/model.txt diff --git a/tests/nebularonezone_inputfiles/recombrates.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/recombrates.txt similarity index 100% rename from tests/nebularonezone_inputfiles/recombrates.txt rename to tests/nebularonezone_1d_3dgrid_inputfiles/recombrates.txt diff --git a/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..2fa661eb9 --- /dev/null +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt @@ -0,0 +1,20 @@ +77a8cefec72da8c0bcebdf4e04d2fcf5 absorption.out +aff44ecad68d986f04fcd6110331ba8b bflist.out +9cfd74fab2244cafd4bce62dc6fe2ad7 deposition.out +986056dc9a141a00e7b62e653b2b4f4b emission.out +8607d1185f06e29637171e28955097e1 emissiontrue.out +9aacf70462a00d805820041d07deaa02 gamma_light_curve.out +035228757b6e30f01ff6a04a6a94d364 gamma_spec.out +057b226c371f3819cba5e04bfea3d114 gammalinelist.out +47dd2e31cc98d5d1dda67520fb399429 grid.out +2c51d13a376994eb179c6e97d44f1dfd light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out +69cfdb90203ff26d32e2de036f464ca9 packets00_0000.out +d50e7c53ed1d845a36909777fc06f2ca packets00_0001.out +7ff320b87eb952c5e24295c2e12a6258 spec.out +8e7163982f1aa938dc1505478b8c60d1 timesteps.out +463e7d1a82b94f8c0fa7083c1f9b59d7 job1/estimators_0000.out +231bdb48880172490892c64690cca72f job1/nlte_0000.out +1c4b1ccb2ea3bd56f3ef976e4dce870e job1/nonthermalspec_0000.out +8d2160b6b8c6c451d9531d7be7cb0243 job1/radfield_0000.out diff --git a/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_job0.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..4650f303d --- /dev/null +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -0,0 +1,19 @@ +f5e5d8fc1f4eb7d1d2e7624e6929954c absorption.out +aff44ecad68d986f04fcd6110331ba8b bflist.out +b1c057449d45d3eafc42a30c2530cb92 deposition.out +91195eb22d4b77ed93bfc5edc96fbd79 emission.out +6bdc6267ae27feab57a34c8a91f78c37 emissiontrue.out +59e10adcf5e9d1c05f935e5583ad9e1e gamma_light_curve.out +057b226c371f3819cba5e04bfea3d114 gammalinelist.out +47dd2e31cc98d5d1dda67520fb399429 grid.out +e0d00b6a32be200c5cf0e9962089c0f3 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out +b0599d01d7bfc71cc14e2fd2fe1bd313 packets00_0000.out +1b9e11a486b6bd9fb0171566fbe73c17 packets00_0001.out +65539a0900e70d998038852567575617 spec.out +8e7163982f1aa938dc1505478b8c60d1 timesteps.out +2a274951a4fff75b7962281cf7bd043c job0/estimators_0000.out +a0850c5580c2ac45a01fefccbc87c702 job0/nlte_0000.out +1c4b1ccb2ea3bd56f3ef976e4dce870e job0/nonthermalspec_0000.out +fc10e7d1fd915e9372566c536d9d4652 job0/radfield_0000.out diff --git a/tests/nebularonezone_1d_3dgrid_inputfiles/syn_dir.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/syn_dir.txt new file mode 100644 index 000000000..ba4514071 --- /dev/null +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/syn_dir.txt @@ -0,0 +1 @@ +0 0 1 \ No newline at end of file diff --git a/tests/nebularonezone_inputfiles/results_md5_final.txt b/tests/nebularonezone_inputfiles/results_md5_final.txt deleted file mode 100644 index c0c14590b..000000000 --- a/tests/nebularonezone_inputfiles/results_md5_final.txt +++ /dev/null @@ -1,20 +0,0 @@ -28a6c36df8d636f510d95bc7271264a1 absorption.out -aff44ecad68d986f04fcd6110331ba8b bflist.out -be0ea11b76c8a0c43d672708dc86eb04 deposition.out -8cc8f6b1e2f8677b8d06f8349275c68b emission.out -887876fedd289e1099430cf25d27a8e7 emissiontrue.out -795e15e87a5becce1de1f13d786102d5 gamma_light_curve.out -d921cc1ced74b4225da7789f1c5689fb gamma_spec.out -057b226c371f3819cba5e04bfea3d114 gammalinelist.out -47dd2e31cc98d5d1dda67520fb399429 grid.out -6897a578c2bc854aa3fe4430fcddc46f light_curve.out -9773becefdfe4afffb041b703210c67c linestat.out -98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -8a27873ed010c86ef5a0025427e0f584 packets00_0000.out -ccfcf24ce1bbc44f6af4c3f5cfec3776 packets00_0001.out -6af813b991bd486cbb780e232fd45a25 spec.out -8e7163982f1aa938dc1505478b8c60d1 timesteps.out -c71682d68f654ac713d30d65d6859e68 job1/estimators_0000.out -1e33b4b6a5bf6a8da1882975f69c77a7 job1/nlte_0000.out -1c4b1ccb2ea3bd56f3ef976e4dce870e job1/nonthermalspec_0000.out -758b51bf02e111b31ec301533cd3bc7b job1/radfield_0000.out \ No newline at end of file diff --git a/tests/nebularonezone_inputfiles/results_md5_job0.txt b/tests/nebularonezone_inputfiles/results_md5_job0.txt deleted file mode 100644 index 5a9f515ce..000000000 --- a/tests/nebularonezone_inputfiles/results_md5_job0.txt +++ /dev/null @@ -1,19 +0,0 @@ -325f0ee7e7b1bbf0b8a5cf401361a987 absorption.out -aff44ecad68d986f04fcd6110331ba8b bflist.out -150cb02fabc4d14108e58eb5b71598d6 deposition.out -8fa4c02a21a902cfab63b9b88fe39f3e emission.out -49b8fe86ed2b3b68fa04624da422c539 emissiontrue.out -c36666a8401b814ea3d785d34cfda8dc gamma_light_curve.out -057b226c371f3819cba5e04bfea3d114 gammalinelist.out -47dd2e31cc98d5d1dda67520fb399429 grid.out -9b992112c19b4378db1c63b0c4a02b1d light_curve.out -9773becefdfe4afffb041b703210c67c linestat.out -98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -78e9c5effc649781796814311221a203 packets00_0000.out -bb6db91444da92febf54c79bae418810 packets00_0001.out -bcb43f7ee46596bd24e0ab90774d93e4 spec.out -8e7163982f1aa938dc1505478b8c60d1 timesteps.out -08eb1796a7f9729f012a3692abd5c5ee job0/estimators_0000.out -5aa018922ab0ed82383d2b909d44cc6c job0/nlte_0000.out -1c4b1ccb2ea3bd56f3ef976e4dce870e job0/nonthermalspec_0000.out -d013568a0d7519e4613fa07607ab0fb5 job0/radfield_0000.out \ No newline at end of file diff --git a/tests/setup_classicmode.sh b/tests/setup_classicmode.sh deleted file mode 100755 index 5da824a82..000000000 --- a/tests/setup_classicmode.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env zsh - -set -x - -runfolder=classicmode_testrun - -mkdir -p $runfolder - -if [ ! -f atomicdata_classic.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_classic.tar.xz; fi - -tar -xf atomicdata_classic.tar.xz --directory $runfolder/ - -rsync -av classicmode_inputfiles/ $runfolder/ - -cp ../data/* $runfolder/ - -cp ../artisoptions_classic.h $runfolder/artisoptions.h - -sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 15000;/g' $runfolder/artisoptions.h - -sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' $runfolder/artisoptions.h - -sed -i'' -e 's/constexpr bool VPKT_ON.*/constexpr bool VPKT_ON = true;/g' $runfolder/artisoptions.h - -set +x diff --git a/tests/setup_classicmode_1d_3dgrid.sh b/tests/setup_classicmode_1d_3dgrid.sh new file mode 100755 index 000000000..2e354f23d --- /dev/null +++ b/tests/setup_classicmode_1d_3dgrid.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=classicmode_1d_3dgrid_testrun + +mkdir -p $runfolder + +if [ ! -f atomicdata_classic.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_classic.tar.xz; fi + +tar -xf atomicdata_classic.tar.xz --directory $runfolder/ + +rsync -av classicmode_1d_3dgrid_inputfiles/ $runfolder/ + +cp ../data/* $runfolder/ + +cp ../artisoptions_classic.h $runfolder/artisoptions.h + +cd $runfolder + +sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 15000;/g' artisoptions.h + +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CARTESIAN3D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 100;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 100;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 100;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool VPKT_ON.*/constexpr bool VPKT_ON = true;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_classicmode_3d.sh b/tests/setup_classicmode_3d.sh index c64f595b8..a5eccd6fc 100755 --- a/tests/setup_classicmode_3d.sh +++ b/tests/setup_classicmode_3d.sh @@ -2,25 +2,34 @@ set -x -rsync -av classicmode_3d_inputfiles/ classicmode_3d_testrun/ +runfolder=classicmode_3d_testrun -xz -d -T0 -v classicmode_3d_testrun/*.xz +mkdir -p $runfolder if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi -tar -xf atomicdata_feconi.tar.xz --directory classicmode_3d_testrun/ +tar -xf atomicdata_feconi.tar.xz --directory $runfolder/ -cp ../data/* classicmode_3d_testrun/ +rsync -av classicmode_3d_inputfiles/ $runfolder/ -cp ../artisoptions_classic.h classicmode_3d_testrun/artisoptions.h +cp ../data/* $runfolder/ -sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 15000;/g' classicmode_3d_testrun/artisoptions.h +cp ../artisoptions_classic.h $runfolder/artisoptions.h -sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 10;/g' classicmode_3d_testrun/artisoptions.h -sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 10;/g' classicmode_3d_testrun/artisoptions.h -sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 10;/g' classicmode_3d_testrun/artisoptions.h +cd $runfolder -sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' classicmode_3d_testrun/artisoptions.h +xz -dv -T0 *.xz +sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 15000;/g' artisoptions.h + +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CARTESIAN3D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 10;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 10;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 10;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h + +cd - set +x diff --git a/tests/setup_kilonova.sh b/tests/setup_kilonova_1d_1dgrid.sh similarity index 57% rename from tests/setup_kilonova.sh rename to tests/setup_kilonova_1d_1dgrid.sh index ef9832d56..c9769965c 100755 --- a/tests/setup_kilonova.sh +++ b/tests/setup_kilonova_1d_1dgrid.sh @@ -2,24 +2,32 @@ set -x -mkdir -p kilonova_testrun +runfolder=kilonova_1d_1dgrid_testrun + +mkdir -p $runfolder if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi -tar -xf atomicdata_feconi.tar.xz --directory kilonova_testrun/ +tar -xf atomicdata_feconi.tar.xz --directory $runfolder/ + +# same input files as the other test run +rsync -av kilonova_1d_3dgrid_inputfiles/ $runfolder/ -rsync -av kilonova_inputfiles/ kilonova_testrun/ +# for the checksum files +rsync -av --ignore-times kilonova_1d_1dgrid_inputfiles/ $runfolder/ -cp ../data/* kilonova_testrun/ +cp ../data/* $runfolder/ -cp ../artisoptions_kilonova_lte.h kilonova_testrun/artisoptions.h +cp ../artisoptions_kilonova_lte.h $runfolder/artisoptions.h -cd kilonova_testrun +cd $runfolder -xz -dvk -T0 *.xz +xz -dv -T0 *.xz sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 80000;/g' artisoptions.h +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_SPHERICAL1D;/g' artisoptions.h + sed -i'' -e 's/constexpr int TABLESIZE.*/constexpr int TABLESIZE = 20;/g' artisoptions.h sed -i'' -e 's/constexpr double MINTEMP.*/constexpr double MINTEMP = 1000.;/g' artisoptions.h sed -i'' -e 's/constexpr double MAXTEMP.*/constexpr double MAXTEMP = 20000.;/g' artisoptions.h diff --git a/tests/setup_kilonova_1d_3dgrid.sh b/tests/setup_kilonova_1d_3dgrid.sh new file mode 100755 index 000000000..700ceebed --- /dev/null +++ b/tests/setup_kilonova_1d_3dgrid.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=kilonova_1d_3dgrid_testrun + +mkdir -p $runfolder + +if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi + +tar -xf atomicdata_feconi.tar.xz --directory $runfolder/ + +rsync -av kilonova_1d_3dgrid_inputfiles/ $runfolder/ + +cp ../data/* $runfolder/ + +cp ../artisoptions_kilonova_lte.h $runfolder/artisoptions.h + +cd $runfolder + +xz -dv -T0 *.xz + +sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 80000;/g' artisoptions.h + +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CARTESIAN3D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 50;/g' artisoptions.h + +sed -i'' -e 's/constexpr int TABLESIZE.*/constexpr int TABLESIZE = 20;/g' artisoptions.h +sed -i'' -e 's/constexpr double MINTEMP.*/constexpr double MINTEMP = 1000.;/g' artisoptions.h +sed -i'' -e 's/constexpr double MAXTEMP.*/constexpr double MAXTEMP = 20000.;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_kilonova_2d_2dgrid.sh b/tests/setup_kilonova_2d_2dgrid.sh new file mode 100755 index 000000000..8530eb8ca --- /dev/null +++ b/tests/setup_kilonova_2d_2dgrid.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=kilonova_2d_2dgrid_testrun + +mkdir -p $runfolder + +if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi + +tar -xf atomicdata_feconi.tar.xz --directory $runfolder/ + +# same input files as the other test run +rsync -av kilonova_2d_3dgrid_inputfiles/ $runfolder/ + +# for the checksum files +rsync -av --ignore-times kilonova_2d_2dgrid_inputfiles/ $runfolder/ + +cp ../data/* $runfolder/ + +cp ../artisoptions_kilonova_lte.h $runfolder/artisoptions.h + +cd $runfolder + +xz -dv -T0 *.xz + +sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 80000;/g' artisoptions.h + +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CYLINDRICAL2D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int TABLESIZE.*/constexpr int TABLESIZE = 20;/g' artisoptions.h +sed -i'' -e 's/constexpr double MINTEMP.*/constexpr double MINTEMP = 1000.;/g' artisoptions.h +sed -i'' -e 's/constexpr double MAXTEMP.*/constexpr double MAXTEMP = 20000.;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_kilonova_2d_3dgrid.sh b/tests/setup_kilonova_2d_3dgrid.sh new file mode 100755 index 000000000..589cfefcd --- /dev/null +++ b/tests/setup_kilonova_2d_3dgrid.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=kilonova_2d_3dgrid_testrun + +mkdir -p $runfolder + +if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi + +tar -xf atomicdata_feconi.tar.xz --directory $runfolder/ + +rsync -av kilonova_2d_3dgrid_inputfiles/ $runfolder/ + +cp ../data/* $runfolder/ + +cp ../artisoptions_kilonova_lte.h $runfolder/artisoptions.h + +cd $runfolder + +xz -dv -T0 *.xz + +sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 80000;/g' artisoptions.h + +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CARTESIAN3D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 50;/g' artisoptions.h + +sed -i'' -e 's/constexpr int TABLESIZE.*/constexpr int TABLESIZE = 20;/g' artisoptions.h +sed -i'' -e 's/constexpr double MINTEMP.*/constexpr double MINTEMP = 1000.;/g' artisoptions.h +sed -i'' -e 's/constexpr double MAXTEMP.*/constexpr double MAXTEMP = 20000.;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_nebularonezone.sh b/tests/setup_nebularonezone_1d_3dgrid.sh similarity index 57% rename from tests/setup_nebularonezone.sh rename to tests/setup_nebularonezone_1d_3dgrid.sh index 7b1a00e19..304ca0fea 100755 --- a/tests/setup_nebularonezone.sh +++ b/tests/setup_nebularonezone_1d_3dgrid.sh @@ -2,20 +2,28 @@ set -x -rsync -av nebularonezone_inputfiles/ nebularonezone_testrun/ +runfolder=nebularonezone_1d_3dgrid_testrun + +rsync -av nebularonezone_1d_3dgrid_inputfiles/ nebularonezone_1d_3dgrid_testrun/ if [ ! -f atomicdata_feconi.tar.xz ]; then curl -O https://theory.gsi.de/~lshingle/artis_http_public/artis/atomicdata_feconi.tar.xz; fi -tar -xf atomicdata_feconi.tar.xz --directory nebularonezone_testrun/ +tar -xf atomicdata_feconi.tar.xz --directory nebularonezone_1d_3dgrid_testrun/ -cp ../data/* nebularonezone_testrun/ +cp ../data/* nebularonezone_1d_3dgrid_testrun/ -cp ../artisoptions_nltenebular.h nebularonezone_testrun/artisoptions.h +cp ../artisoptions_nltenebular.h nebularonezone_1d_3dgrid_testrun/artisoptions.h -cd nebularonezone_testrun +cd nebularonezone_1d_3dgrid_testrun sed -i'' -e 's/constexpr int MPKTS.*/constexpr int MPKTS = 1000000;/g' artisoptions.h +sed -i'' -e 's/constexpr int GRID_TYPE.*/constexpr int GRID_TYPE = GRID_CARTESIAN3D;/g' artisoptions.h + +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_X.*/constexpr int CUBOID_NCOORDGRID_X = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Y.*/constexpr int CUBOID_NCOORDGRID_Y = 50;/g' artisoptions.h +sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGRID_Z = 50;/g' artisoptions.h + sed -i'' -e 's/constexpr int TABLESIZE.*/constexpr int TABLESIZE = 20;/g' artisoptions.h sed -i'' -e 's/constexpr double MINTEMP.*/constexpr double MINTEMP = 2000.;/g' artisoptions.h sed -i'' -e 's/constexpr double MAXTEMP.*/constexpr double MAXTEMP = 10000.;/g' artisoptions.h diff --git a/thermalbalance.cc b/thermalbalance.cc index bae9405ad..8b3f775ce 100644 --- a/thermalbalance.cc +++ b/thermalbalance.cc @@ -1,5 +1,6 @@ #include "thermalbalance.h" +#include #include #include @@ -194,14 +195,16 @@ static auto get_heating_ion_coll_deexc(const int modelgridindex, const int eleme // ---------------------------------------------------------- const int ndowntrans = get_ndowntrans(element, ion, level); for (int i = 0; i < ndowntrans; i++) { - const int lower = globals::elements[element].ions[ion].levels[level].downtrans[i].targetlevelindex; + const auto &downtransition = globals::elements[element].ions[ion].levels[level].downtrans[i]; + const int lower = downtransition.targetlevelindex; const double epsilon_trans = epsilon_level - epsilon(element, ion, lower); - const double C = - nnlevel * col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, i) * epsilon_trans; + const double C = nnlevel * + col_deexcitation_ratecoeff(T_e, nne, epsilon_trans, element, ion, level, downtransition) * + epsilon_trans; C_deexc += C; } } - // const double nnion = ionstagepop(modelgridindex, element, ion); + // const double nnion = get_nnion(modelgridindex, element, ion); // printout("ion_col_deexc_heating: T_e %g nne %g Z=%d ionstage %d nnion %g heating_contrib %g contrib/nnion %g\n", // T_e, nne, get_atomicnumber(element), get_ionstage(element, ion), nnion, C_deexc, C_deexc / nnion); return C_deexc; @@ -348,26 +351,22 @@ static auto T_e_eqn_heating_minus_cooling(const double T_e, void *paras) -> doub /// Set new T_e guess for the current cell and update populations // globals::cell[cellnumber].T_e = T_e; grid::set_Te(modelgridindex, T_e); - double nntot = NAN; - if (NLTE_POPS_ON && NLTE_POPS_ALL_IONS_SIMULTANEOUS) { - nntot = calculate_electron_densities(modelgridindex); - } else { - nntot = calculate_populations(modelgridindex); - } + calculate_ion_balance_nne(modelgridindex); + const auto nne = grid::get_nne(modelgridindex); /// Then calculate heating and cooling rates - const float nne = grid::get_nne(modelgridindex); kpkt::calculate_cooling_rates(modelgridindex, heatingcoolingrates); calculate_heating_rates(modelgridindex, T_e, nne, heatingcoolingrates); - const double nt_frac_heating = nonthermal::get_nt_frac_heating(modelgridindex); - heatingcoolingrates->heating_dep = nonthermal::get_deposition_rate_density(modelgridindex) * nt_frac_heating; - heatingcoolingrates->nt_frac_heating = nt_frac_heating; + heatingcoolingrates->nt_frac_heating = nonthermal::get_nt_frac_heating(modelgridindex); + heatingcoolingrates->heating_dep = + nonthermal::get_deposition_rate_density(modelgridindex) * heatingcoolingrates->nt_frac_heating; /// Adiabatic cooling term - const double p = nntot * KB * T_e; + const double nntot = get_nnion_tot(modelgridindex) + nne; + const double p = nntot * KB * T_e; // pressure in [erg/cm^3] const double volumetmin = grid::get_modelcell_assocvolume_tmin(modelgridindex); - const double dV = 3 * volumetmin / pow(globals::tmin, 3) * pow(t_current, 2); + const double dV = 3 * volumetmin / pow(globals::tmin, 3) * pow(t_current, 2); // really dV/dt const double V = volumetmin * pow(t_current / globals::tmin, 3); // printout("nntot %g, p %g, dV %g, V %g\n",nntot,p,dV,V); heatingcoolingrates->cooling_adiabatic = p * dV / V; @@ -377,7 +376,7 @@ static auto T_e_eqn_heating_minus_cooling(const double T_e, void *paras) -> doub const double total_coolingrate = heatingcoolingrates->cooling_ff + heatingcoolingrates->cooling_fb + heatingcoolingrates->cooling_collisional + heatingcoolingrates->cooling_adiabatic; - return total_heating_rate - total_coolingrate; // - 0.01*(heatingrates_thisthread->bf+coolingrates[tid].fb)/2; + return total_heating_rate - total_coolingrate; } void call_T_e_finder(const int modelgridindex, const int timestep, const double t_current, const double T_min, @@ -450,10 +449,6 @@ void call_T_e_finder(const int modelgridindex, const int timestep, const double MINTEMP, MAXTEMP, modelgridindex, grid::get_TR(modelgridindex), grid::get_W(modelgridindex)); } - if (neutral_flag) { - printout("[info] call_T_e_finder: cell %d contains only neutral ions\n", modelgridindex); - } - if (T_e > 2 * T_e_old) { T_e = 2 * T_e_old; printout("use T_e damping in cell %d\n", modelgridindex); diff --git a/update_grid.cc b/update_grid.cc index db861dd32..12b5ea0fd 100644 --- a/update_grid.cc +++ b/update_grid.cc @@ -1,10 +1,9 @@ #include "update_grid.h" -#include - #include #include "atomic.h" +#include "constants.h" #include "decay.h" #include "grid.h" #include "kpkt.h" @@ -20,24 +19,6 @@ #include "thermalbalance.h" #include "vpkt.h" -void precalculate_partfuncts(int modelgridindex) -/// The partition functions depend only on T_R and W. This means they don't -/// change during any iteration on T_e. Therefore their precalculation was -/// taken out of calculate_populations to save runtime. -{ - /// Precalculate partition functions for each ion in every cell - /// this saves a factor 10 in calculation time of Saha-Boltzman populations - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions; ion++) { - // printout("precalc element %d, ion %d, mgi %d\n",element,ion,modelgridindex); - // globals::cell[cellnumber].composition[element].ltepartfunct[ion] = calculate_ltepartfunct(element,ion,T_R); - grid::modelgrid[modelgridindex].composition[element].partfunct[ion] = - calculate_partfunct(element, ion, modelgridindex); - } - } -} - static void write_to_estimators_file(FILE *estimators_file, const int mgi, const int timestep, const int titer, const struct heatingcoolingrates *heatingcoolingrates) { // return; disable for better performance (if estimators files are not needed) @@ -47,8 +28,8 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const printout("writing to estimators file timestep %d cell %d...\n", timestep, mgi); const auto T_e = grid::get_Te(mgi); - const float nne = grid::get_nne(mgi); - const double Y_e = grid::get_electronfrac(mgi); + const auto nne = grid::get_nne(mgi); + const auto Y_e = grid::get_electronfrac(mgi); // fprintf(estimators_file,"%d %g %g %g %g %d // ",n,get_TR(n),grid::get_Te(n),get_W(n),get_TJ(n),grid::modelgrid[n].thick); fprintf(estimators_file,"%d %g %g %g // %g %g ",n,get_TR(n),grid::get_Te(n),get_W(n),get_TJ(n),grey_optical_depth); @@ -57,12 +38,11 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const "tdays %7.2f\n", timestep, mgi, titer, grid::get_TR(mgi), T_e, grid::get_W(mgi), grid::get_TJ(mgi), grid::modelgrid[mgi].grey_depth, grid::modelgrid[mgi].thick, nne, Y_e, - globals::time_step[timestep].mid / DAY); + globals::timesteps[timestep].mid / DAY); // fprintf(estimators_file,"%d %g %g %g %g %g %g %g //",n,get_TR(n),grid::get_Te(n),get_W(n),get_TJ(n),grey_optical_depth,grey_optical_deptha,compton_optical_depth); - if (NLTE_POPS_ON) // && timestep % 2 == 0 - { + if (globals::total_nlte_levels > 0) { nltepop_write_to_file(mgi, timestep); } @@ -81,15 +61,15 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const } double elpop = 0.; for (int ion = 0; ion < nions; ion++) { - elpop += ionstagepop(mgi, element, ion); - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ionstagepop(mgi, element, ion)); + elpop += get_nnion(mgi, element, ion); + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), get_nnion(mgi, element, ion)); } if (nions == 0) { elpop = grid::get_elem_numberdens(mgi, element); } fprintf(estimators_file, " SUM: %9.3e", elpop); - decay::fprint_nuc_abundances(estimators_file, mgi, globals::time_step[timestep].mid, element); + decay::fprint_nuc_abundances(estimators_file, mgi, globals::timesteps[timestep].mid, element); if (nions == 0 || elpop <= 0.) { // dummy element for nuclear abundances only @@ -371,18 +351,6 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const } fprintf(estimators_file, "\n"); - fprintf(estimators_file, "BF_escfrac_vpkt Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - for (int ion = 0; ion < nions; ion++) { - const double alpha_r_mc = get_ion_stats(mgi, element, ion, stats::ION_RADRECOMB_MACROATOM) + - get_ion_stats(mgi, element, ion, stats::ION_RADRECOMB_KPKT); - const double alpha_r_mc_escaped = get_ion_stats(mgi, element, ion, stats::ION_RADRECOMB_ESCAPED); - fprintf(estimators_file, " %d: %9.3f", get_ionstage(element, ion), alpha_r_mc_escaped / alpha_r_mc); - } - fprintf(estimators_file, "\n"); - fprintf(estimators_file, "BB_escfrac Z=%2d", get_atomicnumber(element)); for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { fprintf(estimators_file, " "); @@ -426,17 +394,17 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const // if (timestep % 20 == 0) // { - // fprintf(estimators_file, "kappa_bf(nuedge) Z=%2d", get_atomicnumber(element)); + // fprintf(estimators_file, "chi_bf(nuedge) Z=%2d", get_atomicnumber(element)); // for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) // fprintf(estimators_file, " "); // for (int ion = 0; ion < nions - 1; ion++) // { // double nu_edge = (epsilon(element, ion + 1, 0) - epsilon(element, ion, 0)) / H; - // double kappa_bf = calculate_kappa_bf_gammacontr(mgi, nu_edge); + // double chi_bf = calculate_chi_bf_gammacontr(mgi, nu_edge, false); // // fprintf(estimators_file, " %d: %9.3e", // get_ionstage(element, ion), - // kappa_bf); + // chi_bf); // } // fprintf(estimators_file, "\n"); // } @@ -658,15 +626,14 @@ static void write_to_estimators_file(FILE *estimators_file, const int mgi, const if (USE_LUT_PHOTOION && globals::nbfcontinua > 0) { fprintf(estimators_file, "corrphotoionrenorm Z=%2d", get_atomicnumber(element)); for (int ion = 0; ion < nions; ion++) { - fprintf( - estimators_file, " %d: %9.3e", get_ionstage(element, ion), - globals::corrphotoionrenorm[mgi * get_nelements() * get_max_nions() + element * get_max_nions() + ion]); + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + globals::corrphotoionrenorm[get_ionestimindex(mgi, element, ion)]); } fprintf(estimators_file, "\n"); fprintf(estimators_file, "gammaestimator Z=%2d", get_atomicnumber(element)); for (int ion = 0; ion < nions; ion++) { fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - globals::gammaestimator[mgi * get_nelements() * get_max_nions() + element * get_max_nions() + ion]); + globals::gammaestimator[get_ionestimindex(mgi, element, ion)]); } fprintf(estimators_file, "\n"); } @@ -707,7 +674,7 @@ void cellhistory_reset(const int modelgridindex, const bool new_timestep) { const int tid = get_thread_num(); // force rpkt opacities to be recalculated next time they are accessed - globals::kappa_rpkt_cont[tid].recalculate_required = true; + globals::chi_rpkt_cont[tid].recalculate_required = true; globals::cellhistory[tid].cellnumber = modelgridindex; @@ -816,111 +783,78 @@ static void solve_Te_nltepops(const int n, const int nts, const int titer, for (int nlte_iter = 0; nlte_iter <= NLTEITER; nlte_iter++) { const time_t sys_time_start_spencerfano = time(nullptr); if (NT_ON && NT_SOLVE_SPENCERFANO) { - nonthermal::solve_spencerfano(n, nts, - nlte_iter); // depends on the ionization balance, and weakly on nne + // SF solution depends on the ionization balance, and weakly on nne + nonthermal::solve_spencerfano(n, nts, nlte_iter); } const int duration_solve_spencerfano = time(nullptr) - sys_time_start_spencerfano; const time_t sys_time_start_partfuncs_or_gamma = time(nullptr); - if (!NLTE_POPS_ON) { - precalculate_partfuncts(n); - } else if (USE_LUT_PHOTOION && (nlte_iter != 0)) { - // recalculate the Gammas using the current population estimates - for (int element = 0; element < get_nelements(); element++) { + for (int element = 0; element < get_nelements(); element++) { + if (!elem_has_nlte_levels(element)) { + calculate_cellpartfuncts(n, element); + } else if (USE_LUT_PHOTOION && (nlte_iter != 0)) { + // recalculate the Gammas using the current population estimates const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { - globals::gammaestimator[n * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = - calculate_iongamma_per_gspop(n, element, ion); + globals::gammaestimator[get_ionestimindex(n, element, ion)] = calculate_iongamma_per_gspop(n, element, ion); } } } const int duration_solve_partfuncs_or_gamma = time(nullptr) - sys_time_start_partfuncs_or_gamma; - /// Find T_e as solution for thermal balance const double prev_T_e = grid::get_Te(n); const time_t sys_time_start_Te = time(nullptr); const int nts_for_te = (titer == 0) ? nts - 1 : nts; - call_T_e_finder(n, nts, globals::time_step[nts_for_te].mid, MINTEMP, MAXTEMP, heatingcoolingrates); + /// Find T_e as solution for thermal balance + call_T_e_finder(n, nts, globals::timesteps[nts_for_te].mid, MINTEMP, MAXTEMP, heatingcoolingrates); const int duration_solve_T_e = time(nullptr) - sys_time_start_Te; - if (!NLTE_POPS_ON || !NLTE_POPS_ALL_IONS_SIMULTANEOUS) // do this in LTE or NLTE single ion solver mode - { - /// Store population values to the grid + if (globals::total_nlte_levels == 0) { const time_t sys_time_start_pops = time(nullptr); - calculate_populations(n); + calculate_ion_balance_nne(n); const int duration_solve_pops = time(nullptr) - sys_time_start_pops; - // calculate_cooling_rates(n); - // calculate_heating_rates(n); + printout( "Grid solver cell %d timestep %d: time spent on: Spencer-Fano %ds, partfuncs/gamma " - "%ds, T_e %ds, " - "populations " - "%ds\n", + "%ds, T_e %ds, populations %ds\n", n, nts, duration_solve_spencerfano, duration_solve_partfuncs_or_gamma, duration_solve_T_e, duration_solve_pops); + break; // no iteration is needed without nlte pops } - if (NLTE_POPS_ON) { + if (globals::total_nlte_levels > 0) { const double fracdiff_T_e = fabs((grid::get_Te(n) / prev_T_e) - 1); const time_t sys_time_start_nltepops = time(nullptr); // fractional difference between previous and current iteration's (nne or max(ground state // population change)) - double nlte_test = 0.; - if (NLTE_POPS_ALL_IONS_SIMULTANEOUS) { - for (int element = 0; element < get_nelements(); element++) { - if (get_nions(element) > 0) { - solve_nlte_pops_element(element, n, nts, nlte_iter); - } - } - } else { - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions - 1; ion++) { - const double trial = fabs(solve_nlte_pops_ion(element, ion, n, nts) - 1); - - if (trial > nlte_test) { - nlte_test = trial; - } - } + double fracdiff_nne = 0.; + for (int element = 0; element < get_nelements(); element++) { + if (get_nions(element) > 0) { + solve_nlte_pops_element(element, n, nts, nlte_iter); + calculate_cellpartfuncts(n, element); } } const int duration_solve_nltepops = time(nullptr) - sys_time_start_nltepops; - if (NLTE_POPS_ALL_IONS_SIMULTANEOUS) { - const double nne_prev = grid::get_nne(n); - precalculate_partfuncts(n); - calculate_electron_densities(n); // sets nne - const double fracdiff_nne = fabs((grid::get_nne(n) / nne_prev) - 1); - nlte_test = fracdiff_nne; - printout( - "NLTE solver cell %d timestep %d iteration %d: time spent on: Spencer-Fano %ds, T_e " - "%ds, NLTE " - "populations " - "%ds\n", - n, nts, nlte_iter, duration_solve_spencerfano, duration_solve_T_e, duration_solve_nltepops); - printout( - "NLTE (Spencer-Fano/Te/pops) solver cell %d timestep %d iteration %d: prev_iter nne " - "%g, new nne is %g, " - "fracdiff %g, prev T_e %g new T_e %g fracdiff %g\n", - n, nts, nlte_iter, nne_prev, grid::get_nne(n), nlte_test, prev_T_e, grid::get_Te(n), fracdiff_T_e); - // damp changes in nne if oscillating to much - // grid::set_nne(n, (grid::get_nne(n) + nne_prev) / 2.); - } else { - printout( - "Completed iteration for NLTE population solver in cell %d for timestep %d. " - "Fractional error returned: " - "%g\n", - n, nts, nlte_test); - } + const double nne_prev = grid::get_nne(n); + calculate_ion_balance_nne(n); // sets nne + fracdiff_nne = fabs((grid::get_nne(n) / nne_prev) - 1); + printout( + "NLTE solver cell %d timestep %d iteration %d: time spent on: Spencer-Fano %ds, T_e " + "%ds, NLTE populations %ds\n", + n, nts, nlte_iter, duration_solve_spencerfano, duration_solve_T_e, duration_solve_nltepops); + printout( + "NLTE (Spencer-Fano/Te/pops) solver cell %d timestep %d iteration %d: prev_iter nne " + "%g, new nne is %g, fracdiff %g, prev T_e %g new T_e %g fracdiff %g\n", + n, nts, nlte_iter, nne_prev, grid::get_nne(n), fracdiff_nne, prev_T_e, grid::get_Te(n), fracdiff_T_e); - if (nlte_test <= covergence_tolerance && fracdiff_T_e <= covergence_tolerance) { + if (fracdiff_nne <= covergence_tolerance && fracdiff_T_e <= covergence_tolerance) { printout( "NLTE (Spencer-Fano/Te/pops) solver nne converged to tolerance %g <= %g and T_e to " - "tolerance %g <= %g " - "after %d iterations.\n", - nlte_test, covergence_tolerance, fracdiff_T_e, covergence_tolerance, nlte_iter + 1); + "tolerance %g <= %g after %d iterations.\n", + fracdiff_nne, covergence_tolerance, fracdiff_T_e, covergence_tolerance, nlte_iter + 1); break; } if (nlte_iter == NLTEITER) { @@ -929,8 +863,6 @@ static void solve_Te_nltepops(const int n, const int nts, const int titer, "last iteration\n", nlte_iter + 1); } - } else { - break; // no iteration is needed without NLTE_POPS_ON } } } @@ -941,11 +873,8 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int n, co for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { - const int ionestimindex = n * get_nelements() * get_max_nions() + element * get_max_nions() + ion; - // printout("mgi %d, element %d, ion %d, gammaest - // %g\n",n,element,ion,globals::gammaestimator[ionestimindex]); + const int ionestimindex = get_ionestimindex(n, element, ion); globals::gammaestimator[ionestimindex] *= estimator_normfactor / H; -// printout("mgi %d, element %d, ion %d, gammaest %g\n",n,element,ion,globals::gammaestimator[ionestimindex]); #ifdef DO_TITER if (globals::gammaestimator_save[ionestimindex] >= 0) { globals::gammaestimator[ionestimindex] = @@ -975,20 +904,16 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int n, co } } if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - /// Then reopen the same loops again. for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { /// Reuse the gammaestimator array as temporary storage of the Gamma values during /// the remaining part of the update_grid phase. Afterwards it is reset to record /// the next timesteps gamma estimators. - // nlevels = get_nlevels(element,ion); - // nlevels = get_ionisinglevels(element,ion); - const int ionestimindex = n * get_nelements() * get_max_nions() + element * get_max_nions() + ion; + const int ionestimindex = get_ionestimindex(n, element, ion); if constexpr (USE_LUT_PHOTOION) { globals::gammaestimator[ionestimindex] = calculate_iongamma_per_gspop(n, element, ion); - // printout("mgi %d, element %d, ion %d, Gamma %g\n",n,element,ion,Gamma); } if constexpr (USE_LUT_BFHEATING) { @@ -1014,9 +939,6 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int n, co element, ion, 0, 0, n, globals::bfheatingestimator[ionestimindex], bfheatingcoeff_ana); abort(); } - - // printout("cell %d element %d ion %d bfheatingestimator - // %g\n",n,element,ion,bfheatingestimator[ionestimindex]); } } } @@ -1041,8 +963,7 @@ static void zero_gammaestimator(const int modelgridindex) { for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { - globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions() + element * get_max_nions() + ion] = - 0.; + globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)] = 0.; } } } @@ -1052,8 +973,7 @@ static void set_all_corrphotoionrenorm(const int modelgridindex, const double va for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { - globals::corrphotoionrenorm[modelgridindex * get_nelements() * get_max_nions() + element * get_max_nions() + - ion] = value; + globals::corrphotoionrenorm[get_ionestimindex(modelgridindex, element, ion)] = value; } } } @@ -1065,224 +985,192 @@ static void update_grid_cell(const int mgi, const int nts, const int nts_prev, c const int assoc_cells = grid::get_numassociatedcells(mgi); if (assoc_cells > 0) { const double deltaV = - grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::time_step[nts_prev].mid / globals::tmin, 3); + grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::timesteps[nts_prev].mid / globals::tmin, 3); const time_t sys_time_start_update_cell = time(nullptr); - /// Update current mass density of cell - // n = nonemptycells[my_rank+ncl*nprocs]; printout("update_grid_cell: working on cell %d before timestep %d titeration %d...\n", mgi, nts, titer); - // n = nonemptycells[ncl]; - // printout("[debug] update_grid: ncl %d is %d non-empty cell updating grid cell %d ... T_e - // %g, rho %g\n",ncl,my_rank+ncl*nprocs,n,globals::cell[n].T_e,globals::cell[n].rho); - grid::modelgrid[mgi].rho = grid::get_rho_tmin(mgi) / pow(tratmid, 3); - // globals::cell[n].rho = globals::cell[n].rho_init / pow(tratmid,3); - // rho = globals::cell[n].rho; - /// This is done outside update grid now - // grid::modelgrid[n].totalcooling = COOLING_UNDEFINED; - - /// Update abundances of radioactive isotopes - decay::update_abundances(mgi, nts, globals::time_step[nts].mid); - const double estimator_normfactor = 1 / deltaV / deltat / globals::nprocs; - const double estimator_normfactor_over4pi = ONEOVER4PI * estimator_normfactor; - if (globals::opacity_case >= 4) { - if (nts == globals::itstep && titer == 0) { - // For the initial timestep, temperatures have already been assigned - // either by trapped energy release calculation, or reading from gridsave file + /// Update current mass density of cell + grid::set_rho(mgi, grid::get_rho_tmin(mgi) / pow(tratmid, 3)); - if constexpr (USE_LUT_PHOTOION) { - /// Determine renormalisation factor for corrected photoionization cross-sections - if (!globals::simulation_continued_from_saved) { - set_all_corrphotoionrenorm(mgi, 1.); - } - } + /// Update elemental abundances with radioactive decays + decay::update_abundances(mgi, nts, globals::timesteps[nts].mid); - /// W == 1 indicates that this modelgrid cell was treated grey in the - /// last timestep. Therefore it has no valid Gamma estimators and must - /// be treated in LTE at restart. - if (grid::modelgrid[mgi].thick == 0 && grid::get_W(mgi) == 1) { - printout( - "force modelgrid cell %d to grey/LTE for update grid since existing W == 1. (will not have gamma " - "estimators)\n", - mgi); - grid::modelgrid[mgi].thick = 1; - } - printout("initial_iteration %d\n", globals::initial_iteration); - printout("mgi %d modelgrid.thick: %d (for this grid update only)\n", mgi, grid::modelgrid[mgi].thick); + const double estimator_normfactor = 1 / deltaV / deltat / globals::nprocs; + const double estimator_normfactor_over4pi = ONEOVER4PI * estimator_normfactor; - precalculate_partfuncts(mgi); + if (globals::opacity_case < 4) { + // various forms of grey opacity + grid::modelgrid[mgi].thick = 1; - if (!globals::simulation_continued_from_saved || !NLTE_POPS_ON || globals::initial_iteration || - grid::modelgrid[mgi].thick == 1) { - calculate_populations(mgi); // these were not read from the gridsave file, so calculate them now + if (globals::opacity_case == 3) { + // printout("update_grid: opacity_case 3 ... updating globals::cell[n].chi_grey"); //MK + if (grid::get_rho(mgi) > globals::rho_crit) { + grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1) * globals::rho_crit / + grid::get_rho(mgi)); } else { - calculate_electron_densities(mgi); + grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1)); } - } else { - /// For all other timesteps temperature corrections have to be applied - - /// we have to calculate the electron density - /// and all the level populations - /// Normalise estimators and make sure that they are finite. - /// Then update T_R and W using the estimators. - /// (This could in principle also be done for empty cells) - - const time_t sys_time_start_temperature_corrections = time(nullptr); - - radfield::normalise_J(mgi, estimator_normfactor_over4pi); // this applies normalisation to the fullspec J - radfield::set_J_normfactor(mgi, - estimator_normfactor_over4pi); // this stores the factor that will be applied - // later for the J bins but not fullspec J + } + } -#ifdef DO_TITER - radfield::titer_J(mgi); -#endif + if (nts == globals::timestep_initial && titer == 0) { + // For the initial timestep, temperatures have already been assigned + // either by trapped energy release calculation, or reading from gridsave file - if constexpr (TRACK_ION_STATS) { - stats::normalise_ion_estimators(mgi, deltat, deltaV); - } + if (USE_LUT_PHOTOION && !globals::simulation_continued_from_saved) { + /// Determine renormalisation factor for corrected photoionization cross-sections + set_all_corrphotoionrenorm(mgi, 1.); + } - // initial_iteration really means either ts 0 or nts < globals::num_lte_timesteps - if (globals::initial_iteration || grid::modelgrid[mgi].thick == 1) { - // LTE mode or grey mode (where temperature doesn't matter but is calculated anyway) + /// W == 1 indicates that this modelgrid cell was treated grey in the + /// last timestep. Therefore it has no valid Gamma estimators and must + /// be treated in LTE at restart. + if (grid::modelgrid[mgi].thick == 0 && grid::get_W(mgi) == 1) { + printout( + "force modelgrid cell %d to grey/LTE for update grid since existing W == 1. (will not have gamma " + "estimators)\n", + mgi); + grid::modelgrid[mgi].thick = 1; + } - const double T_J = radfield::get_T_J_from_J(mgi); - grid::set_TR(mgi, T_J); - grid::set_Te(mgi, T_J); - grid::set_TJ(mgi, T_J); - grid::set_W(mgi, 1); + printout("lte_iteration %d\n", globals::lte_iteration); + printout("mgi %d modelgrid.thick: %d (for this grid update only)\n", mgi, grid::modelgrid[mgi].thick); - if constexpr (USE_LUT_PHOTOION) { - set_all_corrphotoionrenorm(mgi, 1.); - } + for (int element = 0; element < get_nelements(); element++) { + calculate_cellpartfuncts(mgi, element); + } + if (!globals::simulation_continued_from_saved) { + calculate_ion_balance_nne(mgi); + } + } else { + // For all other timesteps temperature corrections have to be applied - precalculate_partfuncts(mgi); - calculate_populations(mgi); - } else // not (initial_iteration || grid::modelgrid[n].thick == 1) - { - // non-LTE timesteps with T_e from heating/cooling + /// we have to calculate the electron density + /// and all the level populations + /// Normalise estimators and make sure that they are finite. + /// Then update T_R and W using the estimators. + /// (This could in principle also be done for empty cells) - radfield::normalise_nuJ(mgi, estimator_normfactor_over4pi); + const time_t sys_time_start_temperature_corrections = time(nullptr); - globals::ffheatingestimator[mgi] *= estimator_normfactor; - globals::colheatingestimator[mgi] *= estimator_normfactor; + radfield::normalise_J(mgi, estimator_normfactor_over4pi); // this applies normalisation to the fullspec J + radfield::set_J_normfactor(mgi, + estimator_normfactor_over4pi); // this stores the factor that will be applied + // later for the J bins but not fullspec J #ifdef DO_TITER - radfield::titer_nuJ(mgi); - titer_average_estimators(mgi); + radfield::titer_J(mgi); #endif - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - update_gamma_corrphotoionrenorm_bfheating_estimators(mgi, estimator_normfactor); - } + if constexpr (TRACK_ION_STATS) { + stats::normalise_ion_estimators(mgi, deltat, deltaV); + } - // Get radiation field parameters (T_J, T_R, W, and bins if enabled) out of the - // full-spectrum and binned J and nuJ estimators - radfield::fit_parameters(mgi, nts); + // lte_iteration really means either ts 0 or nts < globals::num_lte_timesteps + if (globals::lte_iteration || grid::modelgrid[mgi].thick == 1) { + // LTE mode or grey mode (where temperature doesn't matter but is calculated anyway) - if constexpr (DETAILED_BF_ESTIMATORS_ON) { - radfield::normalise_bf_estimators(mgi, estimator_normfactor / H); - } + const double T_J = radfield::get_T_J_from_J(mgi); + grid::set_TR(mgi, T_J); + grid::set_Te(mgi, T_J); + grid::set_TJ(mgi, T_J); + grid::set_W(mgi, 1); - solve_Te_nltepops(mgi, nts, titer, heatingcoolingrates); + if constexpr (USE_LUT_PHOTOION) { + set_all_corrphotoionrenorm(mgi, 1.); } - printout("Temperature/NLTE solution for cell %d timestep %d took %ld seconds\n", mgi, nts, - time(nullptr) - sys_time_start_temperature_corrections); - } - const float nne = grid::get_nne(mgi); - const double compton_optical_depth = SIGMA_T * nne * grid::wid_init(mgi) * tratmid; - - double radial_pos = grid::modelgrid[mgi].initial_radial_pos_sum * tratmid / assoc_cells; - if (GRID_TYPE == GRID_SPHERICAL1D) { - const double r_inner = grid::get_cellcoordmin(mgi, 0) * tratmid; - const double r_outer = r_inner + grid::wid_init(mgi) * tratmid; - radial_pos = 3. / 4 * (pow(r_outer, 4.) - pow(r_inner, 4.)) / - (pow(r_outer, 3) - pow(r_inner, 3.)); // volume averaged mean radius - // printout("r_inner %g r_outer %g tratmid %g assoc_cells %d\n", r_inner, r_outer, - // tratmid, assoc_cells); - } - const double grey_optical_deptha = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * grid::wid_init(mgi) * tratmid; - // cube corners will have radial pos > rmax, so clamp to 0. - const double dist_to_obs = std::max(0., globals::rmax * tratmid - radial_pos); - const double grey_optical_depth = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * dist_to_obs; - printout( - "modelgridcell %d, compton optical depth (/propgridcell) %g, grey optical depth " - "(/propgridcell) %g\n", - mgi, compton_optical_depth, grey_optical_deptha); - printout("radial_pos %g, distance_to_obs %g, tau_dist %g\n", radial_pos, dist_to_obs, grey_optical_depth); + for (int element = 0; element < get_nelements(); element++) { + calculate_cellpartfuncts(mgi, element); + } + calculate_ion_balance_nne(mgi); + } else { + // not lte_iteration and not a thick cell + // non-LTE timesteps with T_e from heating/cooling - grid::modelgrid[mgi].grey_depth = grey_optical_depth; + radfield::normalise_nuJ(mgi, estimator_normfactor_over4pi); - // grey_optical_depth = compton_optical_depth; + globals::ffheatingestimator[mgi] *= estimator_normfactor; + globals::colheatingestimator[mgi] *= estimator_normfactor; - if ((grey_optical_depth >= globals::cell_is_optically_thick) && (nts < globals::num_grey_timesteps)) { - printout("timestep %d cell %d is treated in grey approximation (kappa_grey %g [cm2/g], tau %g >= %g)\n", nts, - mgi, grid::get_kappagrey(mgi), grey_optical_depth, globals::cell_is_optically_thick); - grid::modelgrid[mgi].thick = 1; - } else if (VPKT_ON && (grey_optical_depth > cell_is_optically_thick_vpkt)) { - grid::modelgrid[mgi].thick = 2; - } else { - grid::modelgrid[mgi].thick = 0; - } +#ifdef DO_TITER + radfield::titer_nuJ(mgi); + titer_average_estimators(mgi); +#endif - if (grid::modelgrid[mgi].thick == 1) { - // cooling rates calculation can be skipped for thick cells - // flag with negative numbers to indicate that the rates are invalid - grid::modelgrid[mgi].totalcooling = -1.; - const int element = 0; - const int ion = 0; - grid::modelgrid[mgi].cooling_contrib_ion[element][ion] = -1.; - } else if (globals::simulation_continued_from_saved && nts == globals::itstep) { - // cooling rates were read from the gridsave file for this timestep - // make sure they are valid - assert_always(grid::modelgrid[mgi].totalcooling >= 0.); - const int element = 0; - const int ion = 0; - assert_always(grid::modelgrid[mgi].cooling_contrib_ion[element][ion] >= 0.); - } else { - /// Cooling rates depend only on cell properties, precalculate total cooling - /// and ion contributions inside update grid and communicate between MPI tasks - const time_t sys_time_start_calc_kpkt_rates = time(nullptr); + if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { + update_gamma_corrphotoionrenorm_bfheating_estimators(mgi, estimator_normfactor); + } - printout("calculate_cooling_rates for timestep %d cell %d...", nts, mgi); + // Get radiation field parameters (T_J, T_R, W, and bins if enabled) out of the + // full-spectrum and binned J and nuJ estimators + radfield::fit_parameters(mgi, nts); - // don't pass pointer to heatingcoolingrates because current populations and rates weren't - // used to determine T_e - kpkt::calculate_cooling_rates(mgi, nullptr); + if constexpr (DETAILED_BF_ESTIMATORS_ON) { + radfield::normalise_bf_estimators(mgi, estimator_normfactor / H); + } - printout("took %ld seconds\n", time(nullptr) - sys_time_start_calc_kpkt_rates); + solve_Te_nltepops(mgi, nts, titer, heatingcoolingrates); } - } else { - // For opacity_case != 4 the opacity treatment is grey. Enforce - // optically thick treatment in this case (should be equivalent to grey) + printout("Temperature/NLTE solution for cell %d timestep %d took %ld seconds\n", mgi, nts, + time(nullptr) - sys_time_start_temperature_corrections); + } + + const float nne = grid::get_nne(mgi); + const double compton_optical_depth = SIGMA_T * nne * grid::wid_init(mgi, 0) * tratmid; + + double const radial_pos = grid::modelgrid[mgi].initial_radial_pos_sum * tratmid / assoc_cells; + const double grey_optical_deptha = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * grid::wid_init(mgi, 0) * tratmid; + // cube corners will have radial pos > rmax, so clamp to 0. + const double dist_to_obs = std::max(0., globals::rmax * tratmid - radial_pos); + const double grey_optical_depth = grid::get_kappagrey(mgi) * grid::get_rho(mgi) * dist_to_obs; + printout( + "modelgridcell %d, compton optical depth (/propgridcell) %g, grey optical depth " + "(/propgridcell) %g\n", + mgi, compton_optical_depth, grey_optical_deptha); + printout("radial_pos %g, distance_to_obs %g, tau_dist %g\n", radial_pos, dist_to_obs, grey_optical_depth); + + grid::modelgrid[mgi].grey_depth = grey_optical_depth; + + // grey_optical_depth = compton_optical_depth; + + if ((grey_optical_depth >= globals::cell_is_optically_thick) && (nts < globals::num_grey_timesteps)) { + printout("timestep %d cell %d is treated in grey approximation (chi_grey %g [cm2/g], tau %g >= %g)\n", nts, mgi, + grid::get_kappagrey(mgi), grey_optical_depth, globals::cell_is_optically_thick); grid::modelgrid[mgi].thick = 1; + } else if (VPKT_ON && (grey_optical_depth > cell_is_optically_thick_vpkt)) { + grid::modelgrid[mgi].thick = 2; + } else { + grid::modelgrid[mgi].thick = 0; + } - /// Need the total number density of bound and free electrons for Compton scattering - calculate_electron_densities(mgi); // if this causes problems, disable the nne calculation (only need nne_tot) + if (grid::modelgrid[mgi].thick == 1) { + // cooling rates calculation can be skipped for thick cells + // flag with negative numbers to indicate that the rates are invalid + grid::modelgrid[mgi].totalcooling = -1.; + const int element = 0; + const int ion = 0; + grid::modelgrid[mgi].cooling_contrib_ion[element][ion] = -1.; + } else if (globals::simulation_continued_from_saved && nts == globals::timestep_initial) { + // cooling rates were read from the gridsave file for this timestep + // make sure they are valid + assert_always(grid::modelgrid[mgi].totalcooling >= 0.); + const int element = 0; + const int ion = 0; + assert_always(grid::modelgrid[mgi].cooling_contrib_ion[element][ion] >= 0.); + } else { + /// Cooling rates depend only on cell properties, precalculate total cooling + /// and ion contributions inside update grid and communicate between MPI tasks + const time_t sys_time_start_calc_kpkt_rates = time(nullptr); - if ((nts - globals::itstep) != 0 || titer != 0) { - radfield::normalise_J(mgi, estimator_normfactor_over4pi); // this applies normalisation to the fullspec J - radfield::set_J_normfactor(mgi, - estimator_normfactor_over4pi); // this stores the factor that will be applied - // later for the J bins but not fullspec J + printout("calculate_cooling_rates for timestep %d cell %d...", nts, mgi); - const double T_J = radfield::get_T_J_from_J(mgi); - grid::set_TR(mgi, T_J); - grid::set_Te(mgi, T_J); - grid::set_TJ(mgi, T_J); - grid::set_W(mgi, 1); - } + // don't pass pointer to heatingcoolingrates because current populations and rates weren't + // used to determine T_e + kpkt::calculate_cooling_rates(mgi, nullptr); - if (globals::opacity_case == 3) { - // printout("update_grid: opacity_case 3 ... updating globals::cell[n].kappa_grey"); //MK - if (grid::get_rho(mgi) > globals::rho_crit) { - grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1) * globals::rho_crit / - grid::get_rho(mgi)); - } else { - grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1)); - } - } + printout("took %ld seconds\n", time(nullptr) - sys_time_start_calc_kpkt_rates); } const int update_grid_cell_seconds = time(nullptr) - sys_time_start_update_cell; @@ -1313,12 +1201,12 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const const time_t sys_time_start_update_grid = time(nullptr); printout("\n"); printout("timestep %d: time before update grid %ld (tstart + %ld) simtime ts_mid %g days\n", nts, - sys_time_start_update_grid, sys_time_start_update_grid - real_time_start, globals::time_step[nts].mid / DAY); + sys_time_start_update_grid, sys_time_start_update_grid - real_time_start, globals::timesteps[nts].mid / DAY); if constexpr (USE_LUT_PHOTOION) { /// Initialise globals::corrphotoionrenorm[i] to zero before update_grid is called /// unless they have been read from file - if ((!globals::simulation_continued_from_saved) || (nts - globals::itstep != 0) || (titer != 0)) { + if ((!globals::simulation_continued_from_saved) || (nts - globals::timestep_initial != 0) || (titer != 0)) { printout("nts %d, titer %d: reset corr photoionrenorm\n", nts, titer); for (int i = 0; i < grid::get_npts_model() * get_nelements() * get_max_nions(); i++) { globals::corrphotoionrenorm[i] = 0.; @@ -1328,7 +1216,7 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const } // printout("[debug] update_grid: starting update for timestep %d...\n",m); - const double tratmid = globals::time_step[nts].mid / globals::tmin; + const double tratmid = globals::timesteps[nts].mid / globals::tmin; /// Thread private substitution of max_path_step. Its minimum is /// assigned to max_path_step after the parallel update_grid finished. @@ -1342,14 +1230,14 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const /// regime proportional to the density to a regime independent of the density /// This is done by solving for tau_sobolev == 1 /// tau_sobolev = PI*QE*QE/(ME*C) * rho_crit_para * rho/nucmass(28, 56) * 3000e-8 * - /// globals::time_step[m].mid; + /// globals::timesteps[m].mid; globals::rho_crit = ME * CLIGHT * decay::nucmass(28, 56) / - (PI * QE * QE * globals::rho_crit_para * 3000e-8 * globals::time_step[nts].mid); + (PI * QE * QE * globals::rho_crit_para * 3000e-8 * globals::timesteps[nts].mid); printout("update_grid: rho_crit = %g\n", globals::rho_crit); // These values will not be used if nts == 0, but set them anyway // nts_prev is the previous timestep, unless this is timestep zero - const double deltat = globals::time_step[nts_prev].width; + const double deltat = globals::timesteps[nts_prev].width; // printout("timestep %d, titer %d\n", nts, titer); // printout("deltat %g\n", deltat); @@ -1431,271 +1319,3 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const nts, time(nullptr), my_rank, time_update_grid_end_thisrank - sys_time_start_update_grid, time(nullptr) - time_update_grid_end_thisrank, time(nullptr) - sys_time_start_update_grid); } - -auto calculate_populations(const int modelgridindex) -> double -/// Determines the electron number density for a given cell using one of -/// libgsl's root_solvers and calculates the depending level populations. -{ - /// and the solution function - struct nne_solution_paras paras { - .cellnumber = modelgridindex - }; - gsl_function f{.function = &nne_solution_f, .params = ¶s}; - - neutral_flag = false; - - /// Get temperatures - const double T_R = grid::get_TR(modelgridindex); - const auto T_e = grid::get_Te(modelgridindex); - const double W = grid::get_W(modelgridindex); - - double nne_hi = grid::get_rho(modelgridindex) / MH; - - /// The following section of uppermost_ion is (so far) NOT thread safe!!!!!!!!!!!!!!!!!!!!!!! - int only_neutral_ions = 0; - int nelements_in_cell = 0; - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - // elements[element].uppermost_ion = nions-1; - grid::set_elements_uppermost_ion(modelgridindex, element, nions - 1); - const double abundance = grid::get_elem_abundance(modelgridindex, element); - if (abundance > 0) { - int uppermost_ion = 0; - if (globals::initial_iteration || grid::modelgrid[modelgridindex].thick == 1) { - uppermost_ion = get_nions(element) - 1; - } else { - int ion = 0; - for (ion = 0; ion < nions - 1; ion++) { - double Gamma = 0.; - if constexpr (!USE_LUT_PHOTOION) { - Gamma = calculate_iongamma_per_gspop(modelgridindex, element, ion); - } else { - Gamma = globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions() + - element * get_max_nions() + ion]; - } - - if ((Gamma == 0) && - (!NT_ON || ((globals::rpkt_emiss[modelgridindex] == 0.) && - (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(24, 48)) == 0.) && - (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(28, 56)) == 0.)))) { - break; - } - } - uppermost_ion = ion; - } - - double factor = 1.; - int ion = 0; - for (ion = 0; ion < uppermost_ion; ion++) { - factor *= nne_hi * phi(element, ion, modelgridindex); - // printout("element %d, ion %d, factor %g\n",element,i,factor); - if (!std::isfinite(factor)) { - printout( - "[info] calculate_populations: uppermost_ion limited by phi factors for element " - "Z=%d, ionstage %d in " - "cell %d\n", - get_atomicnumber(element), get_ionstage(element, ion), modelgridindex); - break; - } - } - uppermost_ion = ion; - // printout("cell %d, element %d, final uppermost_ion is %d, factor - // %g\n",modelgridindex,element,uppermost_ion,factor); elements[element].uppermost_ion = - // uppermost_ion; - grid::set_elements_uppermost_ion(modelgridindex, element, uppermost_ion); - if (uppermost_ion == 0) { - only_neutral_ions++; - } - nelements_in_cell++; - } - } - - float nne = 0.; - double nne_tot = 0.; /// total number of electrons in grid cell which are possible - /// targets for compton scattering of gamma rays - double nntot = 0.; - if (only_neutral_ions == nelements_in_cell) { - /// Special case of only neutral ions, set nne to some finite value that - /// packets are not lost in kpkts - /// Introduce a flag variable which is sent to the T_e solver so that - /// we get this info only once when T_e is converged and not for each - /// iteration step. - neutral_flag = true; - // printout("[warning] calculate_populations: only neutral ions in cell %d - // modelgridindex\n",modelgridindex); abort(); - /// Now calculate the ground level populations in nebular approximation and store them to the - /// grid - for (int element = 0; element < get_nelements(); element++) { - /// calculate number density of the current element (abundances are given by mass) - const double nnelement = grid::get_elem_numberdens(modelgridindex, element); - nne_tot += nnelement * get_atomicnumber(element); - - const int nions = get_nions(element); - /// Assign the species population to the neutral ion and set higher ions to MINPOP - for (int ion = 0; ion < nions; ion++) { - double nnion = NAN; - if (ion == 0) { - nnion = nnelement; - } else if (nnelement > 0.) { - nnion = MINPOP; - } else { - nnion = 0.; - } - nntot += nnion; - nne += nnion * (get_ionstage(element, ion) - 1); - grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = - (nnion * stat_weight(element, ion, 0) / - grid::modelgrid[modelgridindex].composition[element].partfunct[ion]); - - if (!std::isfinite(grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion])) { - printout( - "[warning] calculate_populations: groundlevelpop infinite in connection with " - "MINPOP\n"); - } - } - } - nntot += nne; - if (nne < MINPOP) { - nne = MINPOP; - } - grid::set_nne(modelgridindex, nne); - } else { - /// Apply solver to get nne - /// Search solution for nne in [nne_lo,nne_hi] - // printout("nne@x_lo %g\n", nne_solution_f(nne_lo,f.params)); - // printout("nne@x_hi %g\n", nne_solution_f(nne_hi,f.params)); - // printout("n, x_lo, x_hi, T_R, T_e, W, rho %d, %g, %g, %g, %g, %g, - // %g\n",modelgridindex,x_lo,x_hi,T_R,T_e,W,globals::cell[modelgridindex].rho); - double nne_lo = 0.; // MINPOP; - if (nne_solution_f(nne_lo, f.params) * nne_solution_f(nne_hi, f.params) > 0) { - printout("n, nne_lo, nne_hi, T_R, T_e, W, rho %d, %g, %g, %g, %g, %g, %g\n", modelgridindex, nne_lo, nne_hi, T_R, - T_e, W, grid::get_rho(modelgridindex)); - printout("nne@x_lo %g\n", nne_solution_f(nne_lo, f.params)); - printout("nne@x_hi %g\n", nne_solution_f(nne_hi, f.params)); - - for (int element = 0; element < get_nelements(); element++) { - printout("cell %d, element %d, uppermost_ion is %d\n", modelgridindex, element, - grid::get_elements_uppermost_ion(modelgridindex, element)); - - if constexpr (USE_LUT_PHOTOION) { - for (int ion = 0; ion <= grid::get_elements_uppermost_ion(modelgridindex, element); ion++) { - printout("element %d, ion %d, gammaionest %g\n", element, ion, - globals::gammaestimator[modelgridindex * get_nelements() * get_max_nions() + - element * get_max_nions() + ion]); - } - } - } - } - gsl_root_fsolver *solver = gsl_root_fsolver_alloc(gsl_root_fsolver_brent); - - gsl_root_fsolver_set(solver, &f, nne_lo, nne_hi); - constexpr int maxit = 100; - constexpr double fractional_accuracy = 1e-3; - int status = GSL_CONTINUE; - for (int iter = 0; iter <= maxit; iter++) { - iter++; - gsl_root_fsolver_iterate(solver); - nne = gsl_root_fsolver_root(solver); - nne_lo = gsl_root_fsolver_x_lower(solver); - nne_hi = gsl_root_fsolver_x_upper(solver); - status = gsl_root_test_interval(nne_lo, nne_hi, 0, fractional_accuracy); - if (status != GSL_CONTINUE) { - break; - } - } - - gsl_root_fsolver_free(solver); - - if (nne < MINPOP) { - nne = MINPOP; - } - - grid::set_nne(modelgridindex, nne); - if (status == GSL_CONTINUE) { - printout("[warning] calculate_populations: nne did not converge within %d iterations\n", maxit); - } - // printout("[debug] update_grid: status = %s\n",gsl_strerror(status)); - // printout("[debug] update_grid: converged nne %g\n",globals::cell[modelgridindex].nne); - - /// Now calculate the ground level populations in nebular approximation and store them to the - /// grid - // double nne_check = 0.; - nne_tot = 0.; /// total number of electrons in grid cell which are possible - /// targets for compton scattering of gamma rays - - nntot = nne; - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - /// calculate number density of the current element (abundances are given by mass) - const double nnelement = grid::get_elem_numberdens(modelgridindex, element); - nne_tot += nnelement * get_atomicnumber(element); - - const int uppermost_ion = grid::get_elements_uppermost_ion(modelgridindex, element); - auto ionfractions = std::make_unique(uppermost_ion + 1); - - if (nnelement > 0) { - get_ionfractions(element, modelgridindex, nne, ionfractions.get(), uppermost_ion); - } - - /// Use ionizationfractions to calculate the groundlevel populations - for (int ion = 0; ion < nions; ion++) { - double nnion = NAN; - if (ion <= uppermost_ion) { - if (nnelement > 0) { - nnion = nnelement * ionfractions[ion]; - if (nnion < MINPOP) { - nnion = MINPOP; - } - } else { - nnion = 0.; - } - } else { - nnion = MINPOP; /// uppermost_ion is only < nions-1 in cells with nonzero abundance of - /// the given species - } - nntot += nnion; - - grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = - (nnion * stat_weight(element, ion, 0) / - grid::modelgrid[modelgridindex].composition[element].partfunct[ion]); - - if (!std::isfinite(grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion])) { - printout( - "[warning] calculate_populations: groundlevelpop infinite in connection with " - "MINPOP\n"); - } - } - } - } - - grid::set_nnetot(modelgridindex, nne_tot); - return nntot; -} - -auto calculate_electron_densities(const int modelgridindex) -> double -// Determines the free and total electron number densities -// for a given cell and stores them, assuming ion populations (ground_level_pop and partfunc) -// are fixed (determined by NLTE all-ion solver) -{ - double nne_tot = 0.; // total electron density - float nne = 0.; // free electron density - - for (int element = 0; element < get_nelements(); element++) { - // calculate number density of the current element (abundances are given by mass) - const double nnelement = grid::get_elem_numberdens(modelgridindex, element); - nne_tot += nnelement * get_atomicnumber(element); - - // Use ionization fractions to calculate the free electron contributions - if (nnelement > 0) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions; ion++) { - // if (ion <= globals::elements[element].uppermost_ion) - nne += (get_ionstage(element, ion) - 1) * ionstagepop(modelgridindex, element, ion); - } - } - } - - grid::set_nne(modelgridindex, nne); - grid::set_nnetot(modelgridindex, nne_tot); - return nne_tot; -} \ No newline at end of file diff --git a/update_grid.h b/update_grid.h index 826a303f0..c511db41e 100644 --- a/update_grid.h +++ b/update_grid.h @@ -6,9 +6,6 @@ void update_grid(FILE *estimators_file, int nts, int nts_prev, int my_rank, int nstart, int ndo, int titer, time_t real_time_start); -void precalculate_partfuncts(int modelgridindex); void cellhistory_reset(int modelgridindex, bool new_timestep); -double calculate_populations(int modelgridindex); -double calculate_electron_densities(int modelgridindex); #endif // UPDATE_GRID_H diff --git a/update_packets.cc b/update_packets.cc index bd905a504..9002d3a62 100644 --- a/update_packets.cc +++ b/update_packets.cc @@ -63,11 +63,11 @@ static void do_nonthermal_predeposit(struct packet *pkt_ptr, const int nts, cons // absorption happens if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_ALPHA) { - safeadd(globals::time_step[nts].alpha_dep, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].alpha_dep, pkt_ptr->e_cmf); } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAMINUS) { - safeadd(globals::time_step[nts].electron_dep, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].electron_dep, pkt_ptr->e_cmf); } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { - safeadd(globals::time_step[nts].positron_dep, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].positron_dep, pkt_ptr->e_cmf); } pkt_ptr->type = TYPE_NTLEPTON; @@ -90,7 +90,7 @@ static void update_pellet(struct packet *pkt_ptr, const int nts, const double t2 // That's all that needs to be done for the inactive pellet. } else if (tdecay > ts) { // The packet decays in the current timestep. - safeincrement(globals::time_step[nts].pellet_decays); + globals::timesteps[nts].pellet_decays++; pkt_ptr->prop_time = tdecay; vec_scale(pkt_ptr->pos, tdecay / ts); @@ -98,22 +98,22 @@ static void update_pellet(struct packet *pkt_ptr, const int nts, const double t2 if (pkt_ptr->originated_from_particlenotgamma) // will decay to non-thermal particle { if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { - safeadd(globals::time_step[nts].positron_dep, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].positron_dep, pkt_ptr->e_cmf); pkt_ptr->type = TYPE_NTLEPTON; pkt_ptr->absorptiontype = -10; } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAMINUS) { - safeadd(globals::time_step[nts].electron_emission, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].electron_emission, pkt_ptr->e_cmf); pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->type = TYPE_NONTHERMAL_PREDEPOSIT; pkt_ptr->absorptiontype = -10; } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_ALPHA) { - safeadd(globals::time_step[nts].alpha_emission, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].alpha_emission, pkt_ptr->e_cmf); pkt_ptr->em_time = pkt_ptr->prop_time; pkt_ptr->type = TYPE_NONTHERMAL_PREDEPOSIT; pkt_ptr->absorptiontype = -10; } } else { - safeadd(globals::time_step[nts].gamma_emission, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].gamma_emission, pkt_ptr->e_cmf); // decay to gamma-ray, kpkt, or ntlepton gammapkt::pellet_gamma_decay(pkt_ptr); } @@ -152,7 +152,7 @@ static void do_packet(struct packet *const pkt_ptr, const double t2, const int n gammapkt::do_gamma(pkt_ptr, t2); if (pkt_ptr->type != TYPE_GAMMA && pkt_ptr->type != TYPE_ESCAPE) { - safeadd(globals::time_step[nts].gamma_dep, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].gamma_dep, pkt_ptr->e_cmf); } break; } @@ -161,7 +161,7 @@ static void do_packet(struct packet *const pkt_ptr, const double t2, const int n do_rpkt(pkt_ptr, t2); if (pkt_ptr->type == TYPE_ESCAPE) { - safeadd(globals::time_step[nts].cmf_lum, pkt_ptr->e_cmf); + safeadd(globals::timesteps[nts].cmf_lum, pkt_ptr->e_cmf); } break; } @@ -177,13 +177,13 @@ static void do_packet(struct packet *const pkt_ptr, const double t2, const int n } case TYPE_PRE_KPKT: { - kpkt::do_kpkt_bb(pkt_ptr); + kpkt::do_kpkt_blackbody(pkt_ptr); break; } case TYPE_KPKT: { if (grid::modelgrid[grid::get_cell_modelgridindex(pkt_ptr->where)].thick == 1) { - kpkt::do_kpkt_bb(pkt_ptr); + kpkt::do_kpkt_blackbody(pkt_ptr); } else { kpkt::do_kpkt(pkt_ptr, t2, nts); } @@ -250,8 +250,8 @@ void update_packets(const int my_rank, const int nts, struct packet *packets) // matter. Those that are photons (or one sort or another) will already have a position and // a direction. - const double ts = globals::time_step[nts].start; - const double tw = globals::time_step[nts].width; + const double ts = globals::timesteps[nts].start; + const double tw = globals::timesteps[nts].width; const time_t time_update_packets_start = time(nullptr); printout("timestep %d: start update_packets at time %ld\n", nts, time_update_packets_start); @@ -299,7 +299,8 @@ void update_packets(const int my_rank, const int nts, struct packet *packets) const int mgi = grid::get_cell_modelgridindex(cellindex); /// for non empty cells update the global available level populations and cooling terms /// Reset cellhistory if packet starts up in another than the last active cell - if (mgi != grid::get_npts_model() && globals::cellhistory[tid].cellnumber != mgi) { + if (mgi != grid::get_npts_model() && globals::cellhistory[tid].cellnumber != mgi && + grid::modelgrid[mgi].thick == 0) { stats::increment(stats::COUNTER_UPDATECELL); cellhistory_reset(mgi, false); } diff --git a/vectors.cc b/vectors.cc index 623c3cdb2..4b7fd03dc 100644 --- a/vectors.cc +++ b/vectors.cc @@ -1,13 +1,11 @@ #include "vectors.h" -// #include - #include #include "artisoptions.h" #include "sn3d.h" -void scatter_dir(const double dir_in[3], const double cos_theta, double dir_out[3]) +void scatter_dir(std::span dir_in, const double cos_theta, std::span dir_out) // Routine for scattering a direction through angle theta. { // begin with setting the direction in coordinates where original direction @@ -26,7 +24,7 @@ void scatter_dir(const double dir_in[3], const double cos_theta, double dir_out[ // Rotation matrix is determined by dir_in. const double norm1 = 1. / std::sqrt((dir_in[0] * dir_in[0]) + (dir_in[1] * dir_in[1])); - const double norm2 = 1. / std::sqrt((dir_in[0] * dir_in[0]) + (dir_in[1] * dir_in[1]) + (dir_in[2] * dir_in[2])); + const double norm2 = 1. / vec_len(dir_in); const double r11 = dir_in[1] * norm1; const double r12 = -1 * dir_in[0] * norm1; @@ -45,7 +43,7 @@ void scatter_dir(const double dir_in[3], const double cos_theta, double dir_out[ assert_testmodeonly(std::fabs(vec_len(dir_out) - 1.) < 1e-10); } -void get_rand_isotropic_unitvec(double vecout[3]) +void get_rand_isotropic_unitvec(std::span vecout) // Assuming isotropic distribution, get a random direction vector { // alternatively, use GSL's functions: diff --git a/vectors.h b/vectors.h index 078530a70..8b17aae12 100644 --- a/vectors.h +++ b/vectors.h @@ -1,23 +1,28 @@ #ifndef VECTORS_H #define VECTORS_H +#include #include +#include +#include #include "constants.h" #include "exspec.h" #include "packet.h" #include "sn3d.h" -void scatter_dir(const double dir_in[3], double cos_theta, double dir_out[3]); -void get_rand_isotropic_unitvec(double vecout[3]); +void scatter_dir(std::span dir_in, double cos_theta, std::span dir_out); +void get_rand_isotropic_unitvec(std::span vecout); -constexpr double vec_len(const double x[3]) +[[nodiscard]] [[gnu::pure]] constexpr auto vec_len(std::span vec) -> double // return the the magnitude of a vector { - return std::sqrt((x[0] * x[0]) + (x[1] * x[1]) + (x[2] * x[2])); + const double squaredlen = std::accumulate(vec.begin(), vec.end(), 0., [](auto a, auto b) { return a + b * b; }); + + return std::sqrt(squaredlen); } -constexpr void vec_norm(const double vec_in[3], double vec_out[3]) +constexpr void vec_norm(std::span vec_in, std::span vec_out) // normalizing a copy of vec_in and save it to vec_out { const double magnitude = vec_len(vec_in); @@ -29,13 +34,13 @@ constexpr void vec_norm(const double vec_in[3], double vec_out[3]) assert_testmodeonly(fabs(vec_len(vec_out) - 1.) < 1.e-10); } -constexpr double dot(const double x[3], const double y[3]) +[[nodiscard]] [[gnu::pure]] constexpr auto dot(std::span x, std::span y) -> double // vector dot product { - return (x[0] * y[0]) + (x[1] * y[1]) + (x[2] * y[2]); + return std::inner_product(x.begin(), x.end(), y.begin(), 0.); } -constexpr void get_velocity(const double x[3], double y[3], const double t) +constexpr void get_velocity(std::span x, std::span y, const double t) // Routine for getting velocity vector of the flow at a position with homologous expansion. { y[0] = x[0] / t; @@ -43,25 +48,26 @@ constexpr void get_velocity(const double x[3], double y[3], const double t) y[2] = x[2] / t; } -constexpr void cross_prod(const double vec1[3], const double vec2[3], double vecout[3]) { +constexpr void cross_prod(std::span vec1, std::span vec2, + std::span vecout) { vecout[0] = (vec1[1] * vec2[2]) - (vec2[1] * vec1[2]); vecout[1] = (vec1[2] * vec2[0]) - (vec2[2] * vec1[0]); vecout[2] = (vec1[0] * vec2[1]) - (vec2[0] * vec1[1]); } -constexpr void vec_scale(double vec[3], const double scalefactor) { +constexpr void vec_scale(std::span vec, const double scalefactor) { vec[0] *= scalefactor; vec[1] *= scalefactor; vec[2] *= scalefactor; } -constexpr void vec_copy(double destination[3], const double source[3]) { +constexpr void vec_copy(std::span destination, std::span source) { destination[0] = source[0]; destination[1] = source[1]; destination[2] = source[2]; } -constexpr void angle_ab(const double dir1[3], const double vel[3], double dir2[3]) +constexpr void angle_ab(std::span dir1, std::span vel, std::span dir2) // aberation of angles in special relativity // dir1: direction unit vector in frame1 // vel: velocity of frame2 relative to frame1 @@ -81,7 +87,8 @@ constexpr void angle_ab(const double dir1[3], const double vel[3], double dir2[3 vec_norm(dir2, dir2); } -constexpr double doppler_nucmf_on_nurf(const double dir_rf[3], const double vel_rf[3]) +[[gnu::pure]] [[nodiscard]] constexpr double doppler_nucmf_on_nurf(std::span dir_rf, + std::span vel_rf) // Doppler factor // arguments: // dir_rf: the rest frame direction (unit vector) of light propagation @@ -107,15 +114,22 @@ constexpr double doppler_nucmf_on_nurf(const double dir_rf[3], const double vel_ return dopplerfactor; } -constexpr double doppler_squared_nucmf_on_nurf(const double dir_rf[3], const double vel_rf[3]) +[[gnu::pure]] [[nodiscard]] constexpr double doppler_squared_nucmf_on_nurf(std::span pos_rf, + std::span dir_rf, + const double prop_time) // Doppler factor squared, either to first order v/c or fully relativisitic // depending on USE_RELATIVISTIC_DOPPLER_SHIFT // // arguments: +// pos_rf: the rest frame position of the packet // dir_rf: the rest frame direction (unit vector) of light propagation -// vel_rf: velocity of the comoving frame relative to the rest frame +// prop_time: the propagation time of the packet // returns: the ratio f = (nu_cmf / nu_rf) ^ 2 { + // velocity of the comoving frame relative to the rest frame + std::array vel_rf = {0, 0, 0}; // homologous flow velocity + get_velocity(pos_rf, vel_rf, prop_time); + assert_testmodeonly(dot(vel_rf, vel_rf) / CLIGHTSQUARED >= 0.); assert_testmodeonly(dot(vel_rf, vel_rf) / CLIGHTSQUARED < 1.); @@ -137,10 +151,12 @@ constexpr double doppler_squared_nucmf_on_nurf(const double dir_rf[3], const dou return dopplerfactorsq; } -constexpr double doppler_packet_nucmf_on_nurf(const struct packet *const pkt_ptr) { +[[gnu::pure]] [[nodiscard]] constexpr double doppler_packet_nucmf_on_nurf(std::span pos_rf, + std::span dir_rf, + const double prop_time) { double flow_velocity[3] = {0, 0, 0}; // homologous flow velocity - get_velocity(pkt_ptr->pos, flow_velocity, pkt_ptr->prop_time); - return doppler_nucmf_on_nurf(pkt_ptr->dir, flow_velocity); + get_velocity(pos_rf, flow_velocity, prop_time); + return doppler_nucmf_on_nurf(dir_rf, flow_velocity); } constexpr void move_pkt(struct packet *pkt_ptr, const double distance) @@ -156,7 +172,7 @@ constexpr void move_pkt(struct packet *pkt_ptr, const double distance) /// During motion, rest frame energy and frequency are conserved. /// But need to update the co-moving ones. - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr); + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); pkt_ptr->nu_cmf = pkt_ptr->nu_rf * dopplerfactor; pkt_ptr->e_cmf = pkt_ptr->e_rf * dopplerfactor; } @@ -171,12 +187,10 @@ constexpr void move_pkt_withtime(struct packet *pkt_ptr, const double distance) // frequency should only over decrease due to packet movement // enforce this to overcome numerical error - if (pkt_ptr->nu_cmf > nu_cmf_old) { - pkt_ptr->nu_cmf = nu_cmf_old; - } + pkt_ptr->nu_cmf = std::min(pkt_ptr->nu_cmf, nu_cmf_old); } -constexpr double get_arrive_time(const struct packet *pkt_ptr) +[[nodiscard]] [[gnu::pure]] constexpr double get_arrive_time(const struct packet *const pkt_ptr) /// We know that a packet escaped at "escape_time". However, we have /// to allow for travel time. Use the formula in Leon's paper. The extra /// distance to be travelled beyond the reference surface is ds = r_ref (1 - mu). @@ -188,26 +202,26 @@ inline double get_arrive_time_cmf(const struct packet *pkt_ptr) { return pkt_ptr->escape_time * std::sqrt(1. - (globals::vmax * globals::vmax / CLIGHTSQUARED)); } -constexpr int get_escapedirectionbin(const double dir_in[3], const double syn_dir[3]) { +constexpr int get_escapedirectionbin(std::span dir_in, std::span syn_dir) { constexpr double xhat[3] = {1.0, 0.0, 0.0}; // sometimes dir vectors aren't accurately normalised const double dirmag = vec_len(dir_in); - const double dir[3] = {dir_in[0] / dirmag, dir_in[1] / dirmag, dir_in[2] / dirmag}; + std::array dir = {dir_in[0] / dirmag, dir_in[1] / dirmag, dir_in[2] / dirmag}; /// Angle resolved case: need to work out the correct angle bin const double costheta = dot(dir, syn_dir); const int costhetabin = static_cast((costheta + 1.0) * NPHIBINS / 2.0); assert_testmodeonly(costhetabin < NCOSTHETABINS); - double vec1[3] = {0}; + std::array vec1 = {0}; cross_prod(dir, syn_dir, vec1); - double vec2[3] = {0}; + std::array vec2 = {0}; cross_prod(xhat, syn_dir, vec2); const double cosphi = dot(vec1, vec2) / vec_len(vec1) / vec_len(vec2); - double vec3[3] = {0}; + std::array vec3 = {0}; cross_prod(vec2, syn_dir, vec3); const double testphi = dot(vec1, vec3); diff --git a/vpkt.cc b/vpkt.cc index 62467e077..27d98c803 100644 --- a/vpkt.cc +++ b/vpkt.cc @@ -1,26 +1,34 @@ #include "vpkt.h" +#include + +#include #include +#include #include #include "atomic.h" -#include "boundary.h" #include "grid.h" #include "ltepop.h" #include "rpkt.h" #include "sn3d.h" +#include "stats.h" #include "update_grid.h" #include "vectors.h" +struct stokeparams { + double i = 0.; + double q = 0.; + double u = 0.; +}; + struct vspecpol { - double flux[VMNUBINS]; - float lower_time; - float delta_t; + struct stokeparams flux[VMNUBINS]; + float lower_time = NAN; + float delta_t = NAN; }; -struct vspecpol **vstokes_i; -struct vspecpol **vstokes_q; -struct vspecpol **vstokes_u; +struct vspecpol **vspecpol = nullptr; float lower_freq_vspec[VMNUBINS]; float delta_freq_vspec[VMNUBINS]; @@ -29,41 +37,43 @@ float delta_freq_vspec[VMNUBINS]; int Nobs; // Number of observer directions int Nspectra; // Number of virtual packet spectra per observer direction (total + elements switched off) -double *nz_obs_vpkt; -double *phiobs; -double tmin_vspec_input; -double tmax_vspec_input; -int Nrange; - -double numin_vspec_input[MRANGE]; -double numax_vspec_input[MRANGE]; +std::vector nz_obs_vpkt; +std::vector phiobs; +double VSPEC_TIMEMIN_input; +double VSPEC_TIMEMAX_input; +int Nrange; // Number of wavelength ranges + +std::vector VSPEC_NUMIN_input; +std::vector VSPEC_NUMAX_input; double cell_is_optically_thick_vpkt; double tau_max_vpkt; -double *exclude; -double *tau_vpkt; + +std::vector exclude; // vector of opacity contribution setups + //-1: no line opacity; -2: no bf opacity; -3: no ff opacity; -4: no es opacity, + // +ve: exclude element with atomic number's contribution to bound-bound opacity +std::vector tau_vpkt; // --------- Vstruct packet GRID ----------- struct vgrid { - double *flux[MRANGE_GRID]; - double *yvel[MRANGE_GRID]; - double *zvel[MRANGE_GRID]; + std::vector> flux; + double yvel = NAN; + double zvel = NAN; }; -struct vgrid vgrid_i[NY_VGRID][NZ_VGRID]; -struct vgrid vgrid_q[NY_VGRID][NZ_VGRID]; -struct vgrid vgrid_u[NY_VGRID][NZ_VGRID]; +struct vgrid vgrid_i[VGRID_NY][VGRID_NZ]; +struct vgrid vgrid_q[VGRID_NY][VGRID_NZ]; +struct vgrid vgrid_u[VGRID_NY][VGRID_NZ]; int Nrange_grid; double tmin_grid; double tmax_grid; -double nu_grid_min[MRANGE_GRID]; -double nu_grid_max[MRANGE_GRID]; -int vgrid_flag; -double dlogt_vspec; -double dlognu_vspec; +std::vector nu_grid_min; +std::vector nu_grid_max; +bool vgrid_on; -int realtype; +double dlogt_vspec = NAN; +double dlognu_vspec = NAN; // number of virtual packets in a given timestep int nvpkt; @@ -73,63 +83,92 @@ int nvpkt_esc1; // electron scattering event int nvpkt_esc2; // kpkt deactivation int nvpkt_esc3; // macroatom deactivation -void rlc_emiss_vpkt(struct packet *pkt_ptr, double t_current, int bin, double *obs, int realtype) { - double vel_vec[3]; - double old_dir_cmf[3]; - double obs_cmf[3]; - double vel_rev[3]; - double s_cont = NAN; - double kap_cont = NAN; - double kap_cont_nobf = NAN; - double kap_cont_noff = NAN; - double kap_cont_noes = NAN; +// Virtual packet is killed when tau reaches tau_max_vpkt for ALL the different setups +// E.g. imagine that a packet in the first setup (all elements included) reaches tau = tau_max_vpkt +// because of the element Zi. If we remove Zi, tau now could be lower than tau_max_vpkt and could +// thus contribute to the spectrum. +static auto all_taus_past_taumax(std::vector &tau, const double tau_max) -> bool { + return std::ranges::all_of(tau, [tau_max](const double tau_i) { return tau_i > tau_max; }); +} + +// Routine to add a packet to the outcoming spectrum. +static void add_to_vspecpol(const struct packet &vpkt, const int obsbin, const int ind, const double t_arrive) { + // Need to decide in which (1) time and (2) frequency bin the vpkt is escaping + + const int ind_comb = Nspectra * obsbin + ind; + + /// Put this into the time grid. + if (t_arrive > VSPEC_TIMEMIN && t_arrive < VSPEC_TIMEMAX) { + const int nt = static_cast((log(t_arrive) - log(VSPEC_TIMEMIN)) / dlogt_vspec); + if (vpkt.nu_rf > VSPEC_NUMIN && vpkt.nu_rf < VSPEC_NUMAX) { + const int nnu = static_cast((log(vpkt.nu_rf) - log(VSPEC_NUMIN)) / dlognu_vspec); + const double pktcontrib = vpkt.e_rf / vspecpol[nt][ind_comb].delta_t / delta_freq_vspec[nnu] / 4.e12 / PI / + PARSEC / PARSEC / globals::nprocs * 4 * PI; + + safeadd(vspecpol[nt][ind_comb].flux[nnu].i, vpkt.stokes[0] * pktcontrib); + safeadd(vspecpol[nt][ind_comb].flux[nnu].q, vpkt.stokes[1] * pktcontrib); + safeadd(vspecpol[nt][ind_comb].flux[nnu].u, vpkt.stokes[2] * pktcontrib); + } + } +} + +// Routine to add a packet to the outcoming spectrum. +static void add_to_vpkt_grid(const struct packet &vpkt, std::span vel, const int wlbin, + const int obsbin, std::span obs) { + double vref1 = NAN; + double vref2 = NAN; + + // obs is the observer orientation + + // Packet velocity + + // if nobs = x , vref1 = vy and vref2 = vz + if (obs[0] == 1) { + vref1 = vel[1]; + vref2 = vel[2]; + } + // if nobs = -x , vref1 = -vy and vref2 = -vz + else if (obs[0] == -1) { + vref1 = -vel[1]; + vref2 = -vel[2]; + } + + // Rotate velocity into projected area seen by the observer (see notes) + else { + // Rotate velocity from (x,y,z) to (n_obs,ref1,ref2) so that x correspond to n_obs (see notes) + vref1 = -obs[1] * vel[0] + (obs[0] + obs[2] * obs[2] / (1 + obs[0])) * vel[1] - + obs[1] * obs[2] * (1 - obs[0]) / sqrt(1 - obs[0] * obs[0]) * vel[2]; + vref2 = -obs[2] * vel[0] - obs[1] * obs[2] * (1 - obs[0]) / sqrt(1 - obs[0] * obs[0]) * vel[1] + + (obs[0] + obs[1] * obs[1] / (1 + obs[0])) * vel[2]; + } + + // Outside the grid + if (fabs(vref1) >= globals::vmax || fabs(vref2) >= globals::vmax) { + return; + } + + // Bin size + + // vgrid cell (can be different to propagation cell size) + const int ny = static_cast((globals::vmax - vref1) / (2 * globals::vmax / VGRID_NY)); + const int nz = static_cast((globals::vmax - vref2) / (2 * globals::vmax / VGRID_NZ)); + + // Add contribution + if (vpkt.nu_rf > nu_grid_min[wlbin] && vpkt.nu_rf < nu_grid_max[wlbin]) { + safeadd(vgrid_i[ny][nz].flux[wlbin][obsbin], vpkt.stokes[0] * vpkt.e_rf); + safeadd(vgrid_q[ny][nz].flux[wlbin][obsbin], vpkt.stokes[1] * vpkt.e_rf); + safeadd(vgrid_u[ny][nz].flux[wlbin][obsbin], vpkt.stokes[2] * vpkt.e_rf); + } +} + +static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_current, const int obsbin, + std::span obsdir, const enum packet_type realtype) { int snext = 0; - double t_arrive = NAN; - int element = 0; - int ion = 0; - int upper = 0; - int lower = 0; - double A_ul = NAN; - double B_ul = NAN; - double B_lu = NAN; - double n_u = NAN; - double n_l = NAN; - double t_line = NAN; int mgi = 0; - double Qold = NAN; - double Uold = NAN; - double Inew = NAN; - double Qnew = NAN; - double Unew = NAN; - double Itmp = NAN; - double Qtmp = NAN; - double Utmp = NAN; - double I = NAN; - double Q = NAN; - double U = NAN; - double pn = NAN; - double prob = NAN; - double mu = NAN; - double i1 = NAN; - double i2 = NAN; - double cos2i1 = NAN; - double sin2i1 = NAN; - double cos2i2 = NAN; - double sin2i2 = NAN; - double ref1[3]; - double ref2[3]; - int anumber = 0; - int tau_flag = 0; - - int bin_range = 0; - - struct packet dummy; - dummy = *pkt_ptr; - struct packet *dummy_ptr = nullptr; - dummy_ptr = &dummy; + + struct packet vpkt = *pkt_ptr; bool end_packet = false; - double sdist = 0; double ldist = 0; double t_future = t_current; @@ -137,53 +176,64 @@ void rlc_emiss_vpkt(struct packet *pkt_ptr, double t_current, int bin, double *o tau_vpkt[ind] = 0; } - dummy_ptr->dir[0] = obs[0]; - dummy_ptr->dir[1] = obs[1]; - dummy_ptr->dir[2] = obs[2]; + vpkt.dir[0] = obsdir[0]; + vpkt.dir[1] = obsdir[1]; + vpkt.dir[2] = obsdir[2]; + vpkt.last_cross = BOUNDARY_NONE; safeincrement(nvpkt); // increment the number of virtual packet in the given timestep + double vel_vec[3] = {NAN, NAN, NAN}; get_velocity(pkt_ptr->pos, vel_vec, t_current); // rf frequency and energy - dummy_ptr->nu_rf = dummy_ptr->nu_cmf / doppler_nucmf_on_nurf(dummy_ptr->dir, vel_vec); - dummy_ptr->e_rf = dummy_ptr->e_cmf * dummy_ptr->nu_rf / dummy_ptr->nu_cmf; + const double dopplerfactor = doppler_nucmf_on_nurf(vpkt.dir, vel_vec); + vpkt.nu_rf = vpkt.nu_cmf / dopplerfactor; + vpkt.e_rf = vpkt.e_cmf / dopplerfactor; - double Qi = dummy_ptr->stokes[1]; - double Ui = dummy_ptr->stokes[2]; + double Qi = vpkt.stokes[1]; + double Ui = vpkt.stokes[2]; // ------------ SCATTERING EVENT: dipole function -------------------- - if (realtype == 1) { + double ref1[3] = {NAN, NAN, NAN}; + double ref2[3] = {NAN, NAN, NAN}; + double pn = NAN; + double I = NAN; + double Q = NAN; + double U = NAN; + if (realtype == TYPE_RPKT) { // Transform Stokes Parameters from the RF to the CMF + double old_dir_cmf[3] = {NAN, NAN, NAN}; frame_transform(pkt_ptr->dir, &Qi, &Ui, vel_vec, old_dir_cmf); // Need to rotate Stokes Parameters in the scattering plane - angle_ab(dummy_ptr->dir, vel_vec, obs_cmf); + double obs_cmf[3]; + angle_ab(vpkt.dir, vel_vec, obs_cmf); meridian(old_dir_cmf, ref1, ref2); // This is the i1 angle of Bulla+2015, obtained by computing the angle between the // reference axes ref1 and ref2 in the meridian frame and the corresponding axes // ref1_sc and ref2_sc in the scattering plane. - i1 = rot_angle(old_dir_cmf, obs_cmf, ref1, ref2); - cos2i1 = cos(2 * i1); - sin2i1 = sin(2 * i1); + const double i1 = rot_angle(old_dir_cmf, obs_cmf, ref1, ref2); + const double cos2i1 = cos(2 * i1); + const double sin2i1 = sin(2 * i1); - Qold = Qi * cos2i1 - Ui * sin2i1; - Uold = Qi * sin2i1 + Ui * cos2i1; + const double Qold = Qi * cos2i1 - Ui * sin2i1; + const double Uold = Qi * sin2i1 + Ui * cos2i1; // Scattering - mu = dot(old_dir_cmf, obs_cmf); + const double mu = dot(old_dir_cmf, obs_cmf); pn = 3. / (16. * PI) * (1 + pow(mu, 2.) + (pow(mu, 2.) - 1) * Qold); - Inew = 0.75 * ((mu * mu + 1.0) + Qold * (mu * mu - 1.0)); - Qnew = 0.75 * ((mu * mu - 1.0) + Qold * (mu * mu + 1.0)); - Unew = 1.5 * mu * Uold; + const double Inew = 0.75 * ((mu * mu + 1.0) + Qold * (mu * mu - 1.0)); + double Qnew = 0.75 * ((mu * mu - 1.0) + Qold * (mu * mu + 1.0)); + double Unew = 1.5 * mu * Uold; Qnew = Qnew / Inew; Unew = Unew / Inew; @@ -196,130 +246,124 @@ void rlc_emiss_vpkt(struct packet *pkt_ptr, double t_current, int bin, double *o // This is the i2 angle of Bulla+2015, obtained from the angle THETA between the // reference axes ref1_sc and ref2_sc in the scattering plane and ref1 and ref2 in the // meridian frame. NB: we need to add PI to transform THETA to i2 - i2 = PI + rot_angle(obs_cmf, old_dir_cmf, ref1, ref2); - cos2i2 = cos(2 * i2); - sin2i2 = sin(2 * i2); + const double i2 = PI + rot_angle(obs_cmf, old_dir_cmf, ref1, ref2); + const double cos2i2 = cos(2 * i2); + const double sin2i2 = sin(2 * i2); Q = Qnew * cos2i2 + Unew * sin2i2; U = -Qnew * sin2i2 + Unew * cos2i2; // Transform Stokes Parameters from the CMF to the RF - vel_rev[0] = -vel_vec[0]; - vel_rev[1] = -vel_vec[1]; - vel_rev[2] = -vel_vec[2]; + const double vel_rev[3] = {-vel_vec[0], -vel_vec[1], -vel_vec[2]}; - frame_transform(obs_cmf, &Q, &U, vel_rev, obs); - } - - // ------------ MACROATOM and KPKT: isotropic emission -------------------- + frame_transform(obs_cmf, &Q, &U, vel_rev, obsdir); - if (realtype == 2 || realtype == 3) { + } else if (realtype == TYPE_KPKT || realtype == TYPE_MA) { + // MACROATOM and KPKT: isotropic emission I = 1; Q = 0; U = 0; pn = 1 / (4 * PI); } - // --------- compute the optical depth to boundary ---------------- + // compute the optical depth to boundary - mgi = grid::get_cell_modelgridindex(dummy_ptr->where); + mgi = grid::get_cell_modelgridindex(vpkt.where); + struct rpkt_continuum_absorptioncoeffs chi_vpkt_cont = {}; while (!end_packet) { - ldist = 0; - // distance to the next cell - sdist = boundary_cross(dummy_ptr, &snext); - s_cont = sdist * t_current * t_current * t_current / (t_future * t_future * t_future); - - calculate_kappa_rpkt_cont(dummy_ptr, &globals::kappa_rpkt_cont[tid]); - - kap_cont = globals::kappa_rpkt_cont[tid].total; - kap_cont_nobf = kap_cont - globals::kappa_rpkt_cont[tid].bf; - kap_cont_noff = kap_cont - globals::kappa_rpkt_cont[tid].ff; - kap_cont_noes = kap_cont - globals::kappa_rpkt_cont[tid].es; - - for (int ind = 0; ind < Nspectra; ind++) { - if (exclude[ind] == -2) { - tau_vpkt[ind] += kap_cont_nobf * s_cont; - } else if (exclude[ind] == -3) { - tau_vpkt[ind] += kap_cont_noff * s_cont; - } else if (exclude[ind] == -4) { - tau_vpkt[ind] += kap_cont_noes * s_cont; - } else { - tau_vpkt[ind] += kap_cont * s_cont; - } - } - - // kill vpkt with high optical depth - tau_flag = check_tau(tau_vpkt, &tau_max_vpkt); - if (tau_flag == 0) { - return; - } + const double sdist = + grid::boundary_distance(vpkt.dir, vpkt.pos, vpkt.prop_time, vpkt.where, &snext, &vpkt.last_cross); + const double s_cont = sdist * t_current * t_current * t_current / (t_future * t_future * t_future); - while (ldist < sdist) { - // printout("next_trans = %d \t nutrans = %g \t",dummy_ptr->next_trans,nutrans); - - const int lineindex = closest_transition(dummy_ptr->nu_cmf, dummy_ptr->next_trans); - - const double nutrans = globals::linelist[lineindex].nu; - - if (lineindex >= 0) { - element = globals::linelist[lineindex].elementindex; - ion = globals::linelist[lineindex].ionindex; - upper = globals::linelist[lineindex].upperlevelindex; - lower = globals::linelist[lineindex].lowerlevelindex; - A_ul = globals::linelist[lineindex].einstein_A; - - anumber = get_atomicnumber(element); - - dummy_ptr->next_trans = lineindex + 1; - - if (dummy_ptr->nu_cmf < nutrans) { - ldist = 0; + if (mgi == grid::get_npts_model()) { + vpkt.next_trans = -1; + } else { + calculate_chi_rpkt_cont(vpkt.nu_cmf, &chi_vpkt_cont, mgi, false); + + const double chi_cont = chi_vpkt_cont.total; + + for (int ind = 0; ind < Nspectra; ind++) { + if (exclude[ind] == -2) { + const double chi_cont_nobf = chi_cont - chi_vpkt_cont.bf; + tau_vpkt[ind] += chi_cont_nobf * s_cont; + } else if (exclude[ind] == -3) { + const double chi_cont_noff = chi_cont - chi_vpkt_cont.ff; + tau_vpkt[ind] += chi_cont_noff * s_cont; + } else if (exclude[ind] == -4) { + const double chi_cont_noes = chi_cont - chi_vpkt_cont.es; + tau_vpkt[ind] += chi_cont_noes * s_cont; } else { - ldist = CLIGHT * t_current * (dummy_ptr->nu_cmf / nutrans - 1); + tau_vpkt[ind] += chi_cont * s_cont; } + } - if (ldist < 0.) { - printout("[warning] get_event: ldist < 0 %g\n", ldist); - } + // kill vpkt with high optical depth + if (all_taus_past_taumax(tau_vpkt, tau_max_vpkt)) { + return; + } - if (ldist > sdist) { /* exit the while loop if you reach the boundary; go back to the previous transition to - start next cell with the excluded line */ + struct packet dummypkt_abort = vpkt; + move_pkt_withtime(&dummypkt_abort, sdist); + const double nu_cmf_abort = dummypkt_abort.nu_cmf; + assert_testmodeonly(nu_cmf_abort <= vpkt.nu_cmf); + const double d_nu_on_d_l = (nu_cmf_abort - vpkt.nu_cmf) / sdist; - dummy_ptr->next_trans -= 1; - // printout("ldist > sdist : line in the next cell\n"); - break; - } + ldist = 0; + while (ldist < sdist) { + const int lineindex = closest_transition(vpkt.nu_cmf, vpkt.next_trans); + + if (lineindex < 0) { + vpkt.next_trans = globals::nlines + 1; + } else { + const double nutrans = globals::linelist[lineindex].nu; - t_line = t_current + ldist / CLIGHT; + vpkt.next_trans = lineindex + 1; - B_ul = CLIGHTSQUAREDOVERTWOH / pow(nutrans, 3) * A_ul; - B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; + ldist = get_linedistance(t_current, vpkt.nu_cmf, nutrans, d_nu_on_d_l); - n_u = get_levelpop(mgi, element, ion, upper); - n_l = get_levelpop(mgi, element, ion, lower); + if (ldist > sdist) { + // exit the while loop if you reach the boundary; go back to the previous transition to start next cell with + // the excluded line - // Check on the element to exclude - // NB: ldist before need to be computed anyway (I want to move the packets to the - // line interaction point even if I don't interact) + vpkt.next_trans -= 1; + // printout("ldist > sdist : line in the next cell\n"); + break; + } - for (int ind = 0; ind < Nspectra; ind++) { - // If exclude[ind]==-1, I do not include line opacity - if (exclude[ind] != -1 && (anumber != exclude[ind])) { - tau_vpkt[ind] += (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_line; + const double t_line = t_current + ldist / CLIGHT; + + const int element = globals::linelist[lineindex].elementindex; + const int ion = globals::linelist[lineindex].ionindex; + const int upper = globals::linelist[lineindex].upperlevelindex; + const int lower = globals::linelist[lineindex].lowerlevelindex; + const auto A_ul = globals::linelist[lineindex].einstein_A; + + const double B_ul = CLIGHTSQUAREDOVERTWOH / pow(nutrans, 3) * A_ul; + const double B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; + + const auto n_u = calculate_levelpop(mgi, element, ion, upper); + const auto n_l = calculate_levelpop(mgi, element, ion, lower); + const double tau_line = (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_line; + + // Check on the element to exclude + // NB: ldist before need to be computed anyway (I want to move the packets to the + // line interaction point even if I don't interact) + const int anumber = get_atomicnumber(element); + for (int ind = 0; ind < Nspectra; ind++) { + // If exclude[ind]==-1, I do not include line opacity + if (exclude[ind] != -1 && (exclude[ind] != anumber)) { + tau_vpkt[ind] += tau_line; + } } - } - /* kill vpkt with high optical depth */ - tau_flag = check_tau(tau_vpkt, &tau_max_vpkt); - if (tau_flag == 0) { - return; + // kill vpkt with high optical depth + if (all_taus_past_taumax(tau_vpkt, tau_max_vpkt)) { + return; + } } - } else { - dummy_ptr->next_trans = - globals::nlines + 1; /// helper variable to overcome numerical problems after line scattering } } @@ -328,169 +372,115 @@ void rlc_emiss_vpkt(struct packet *pkt_ptr, double t_current, int bin, double *o // printf("I'm changing cell. I'm going from nu_cmf = %.e ",dummy_ptr->nu_cmf); t_future += (sdist / CLIGHT_PROP); - dummy_ptr->prop_time = t_future; - move_pkt(dummy_ptr, sdist); + vpkt.prop_time = t_future; + move_pkt(&vpkt, sdist); - // printout("About to change vpkt cell\n"); - change_cell(dummy_ptr, snext); - end_packet = (dummy_ptr->type == TYPE_ESCAPE); - // printout("Completed change vpkt cell\n"); + grid::change_cell(&vpkt, snext); + end_packet = (vpkt.type == TYPE_ESCAPE); - // printout("dummy->nu_cmf = %g \n",dummy_ptr->nu_cmf); - mgi = grid::get_cell_modelgridindex(dummy_ptr->where); + mgi = grid::get_cell_modelgridindex(vpkt.where); // break if you reach an empty cell if (mgi == grid::get_npts_model()) { break; } - /* kill vpkt with pass through a thick cell */ - if (grid::modelgrid[mgi].thick == 1) { + // kill vpkt with pass through a thick cell + if (grid::modelgrid[mgi].thick != 0) { return; } } // increment the number of escaped virtual packet in the given timestep - if (realtype == 1) { + if (realtype == TYPE_RPKT) { safeincrement(nvpkt_esc1); - } else if (realtype == 2) { + } else if (realtype == TYPE_KPKT) { safeincrement(nvpkt_esc2); - } else if (realtype == 3) { + } else if (realtype == TYPE_MA) { safeincrement(nvpkt_esc3); } + const double t_arrive = t_current - (dot(pkt_ptr->pos, vpkt.dir) / CLIGHT_PROP); // -------------- final stokes vector --------------- for (int ind = 0; ind < Nspectra; ind++) { - // printout("bin %d spectrum %d tau_vpkt %g\n", bin, ind, tau_vpkt[ind]); - prob = pn * exp(-tau_vpkt[ind]); + // printout("obsbin %d spectrum %d tau_vpkt %g\n", obsbin, ind, tau_vpkt[ind]); + const double prob = pn * exp(-tau_vpkt[ind]); - Itmp = I * prob; - Qtmp = Q * prob; - Utmp = U * prob; + assert_always(std::isfinite(prob)); - dummy_ptr->stokes[0] = Itmp; - dummy_ptr->stokes[1] = Qtmp; - dummy_ptr->stokes[2] = Utmp; + vpkt.stokes[0] = I * prob; + vpkt.stokes[1] = Q * prob; + vpkt.stokes[2] = U * prob; - if (Itmp != Itmp || Qtmp != Qtmp || Utmp != Utmp) { - printout("Nan Number!! %g %g %g %g %g %g %g %g \n", Itmp, Qtmp, Utmp, pn, tau_vpkt[ind], mu, i1, i2); + for (const auto stokeval : vpkt.stokes) { + assert_always(std::isfinite(stokeval)); } // bin on fly and produce file with spectrum - t_arrive = t_current - (dot(pkt_ptr->pos, dummy_ptr->dir) / CLIGHT_PROP); - - add_to_vspecpol(dummy_ptr, bin, ind, t_arrive); + add_to_vspecpol(vpkt, obsbin, ind, t_arrive); } // vpkt grid - if (vgrid_flag == 1) { - prob = pn * exp(-tau_vpkt[0]); - - Itmp = I * prob; - Qtmp = Q * prob; - Utmp = U * prob; + if (vgrid_on) { + const double prob = pn * exp(-tau_vpkt[0]); - dummy_ptr->stokes[0] = Itmp; - dummy_ptr->stokes[1] = Qtmp; - dummy_ptr->stokes[2] = Utmp; + vpkt.stokes[0] = I * prob; + vpkt.stokes[1] = Q * prob; + vpkt.stokes[2] = U * prob; - for (bin_range = 0; bin_range < Nrange_grid; bin_range++) { - if (dummy_ptr->nu_rf > nu_grid_min[bin_range] && - dummy_ptr->nu_rf < nu_grid_max[bin_range]) { // Frequency selection - if (t_arrive > tmin_grid && t_arrive < tmax_grid) { // Time selection - add_to_vpkt_grid(dummy_ptr, vel_vec, bin_range, bin, obs); + for (int wlbin = 0; wlbin < Nrange_grid; wlbin++) { + if (vpkt.nu_rf > nu_grid_min[wlbin] && vpkt.nu_rf < nu_grid_max[wlbin]) { // Frequency selection + if (t_arrive > tmin_grid && t_arrive < tmax_grid) { // Time selection + add_to_vpkt_grid(vpkt, vel_vec, wlbin, obsbin, obsdir); } } } } } -// Virtual packet is killed when tau reaches tau_max_vpkt for ALL the different setups -// E.g. imagine that a packet in the first setup (all elements included) reaches tau = tau_max_vpkt -// because of the element Zi. If we remove Zi, tau now could be lower than tau_max_vpkt and could -// thus contribute to the spectrum. -auto check_tau(const double *tau, const double *tau_max) -> int { - int count = 0; - - for (int i = 0; i < Nspectra; i++) { - if (tau[i] > *tau_max) { - count += 1; - } - } - - if (count == Nspectra) { - return 0; - } - return 1; -} - -// Routine to add a packet to the outcoming spectrum. -void add_to_vspecpol(struct packet *pkt_ptr, int bin, int ind, double t_arrive) { - // Need to decide in which (1) time and (2) frequency bin the vpkt is escaping - - const int ind_comb = Nspectra * bin + ind; - - /// Put this into the time grid. - if (t_arrive > tmin_vspec && t_arrive < tmax_vspec) { - const int nt = static_cast((log(t_arrive) - log(tmin_vspec)) / dlogt_vspec); - if (pkt_ptr->nu_rf > numin_vspec && pkt_ptr->nu_rf < numax_vspec) { - const int nnu = static_cast((log(pkt_ptr->nu_rf) - log(numin_vspec)) / dlognu_vspec); - const double pktcontrib = pkt_ptr->e_rf / vstokes_i[nt][ind_comb].delta_t / delta_freq_vspec[nnu] / 4.e12 / PI / - PARSEC / PARSEC / globals::nprocs * 4 * PI; - - safeadd(vstokes_i[nt][ind_comb].flux[nnu], pkt_ptr->stokes[0] * pktcontrib); - safeadd(vstokes_q[nt][ind_comb].flux[nnu], pkt_ptr->stokes[1] * pktcontrib); - safeadd(vstokes_u[nt][ind_comb].flux[nnu], pkt_ptr->stokes[2] * pktcontrib); - } - } -} - -void init_vspecpol() { - vstokes_i = static_cast(malloc(VMTBINS * sizeof(struct vspecpol *))); - vstokes_q = static_cast(malloc(VMTBINS * sizeof(struct vspecpol *))); - vstokes_u = static_cast(malloc(VMTBINS * sizeof(struct vspecpol *))); +static void init_vspecpol() { + vspecpol = static_cast(malloc(VMTBINS * sizeof(struct vspecpol *))); const int indexmax = Nspectra * Nobs; for (int p = 0; p < VMTBINS; p++) { - vstokes_i[p] = static_cast(malloc(indexmax * sizeof(struct vspecpol))); - vstokes_q[p] = static_cast(malloc(indexmax * sizeof(struct vspecpol))); - vstokes_u[p] = static_cast(malloc(indexmax * sizeof(struct vspecpol))); + vspecpol[p] = static_cast(malloc(indexmax * sizeof(struct vspecpol))); } - for (int ind_comb = 0; ind_comb < indexmax; ind_comb++) { - // start by setting up the time and frequency bins. - // it is all done interms of a logarithmic spacing in both t and nu - get the - // step sizes first. - - dlogt_vspec = (log(tmax_vspec) - log(tmin_vspec)) / VMTBINS; - dlognu_vspec = (log(numax_vspec) - log(numin_vspec)) / VMNUBINS; - - for (int n = 0; n < VMTBINS; n++) { - vstokes_i[n][ind_comb].lower_time = exp(log(tmin_vspec) + (n * (dlogt_vspec))); - vstokes_i[n][ind_comb].delta_t = - exp(log(tmin_vspec) + ((n + 1) * (dlogt_vspec))) - vstokes_i[n][ind_comb].lower_time; + dlogt_vspec = (log(VSPEC_TIMEMAX) - log(VSPEC_TIMEMIN)) / VMTBINS; + dlognu_vspec = (log(VSPEC_NUMAX) - log(VSPEC_NUMIN)) / VMNUBINS; - for (int m = 0; m < VMNUBINS; m++) { - lower_freq_vspec[m] = exp(log(numin_vspec) + (m * (dlognu_vspec))); - delta_freq_vspec[m] = exp(log(numin_vspec) + ((m + 1) * (dlognu_vspec))) - lower_freq_vspec[m]; + for (int m = 0; m < VMNUBINS; m++) { + lower_freq_vspec[m] = exp(log(VSPEC_NUMIN) + (m * (dlognu_vspec))); + delta_freq_vspec[m] = exp(log(VSPEC_NUMIN) + ((m + 1) * (dlognu_vspec))) - lower_freq_vspec[m]; + } - vstokes_i[n][ind_comb].flux[m] = 0.0; - vstokes_q[n][ind_comb].flux[m] = 0.0; - vstokes_u[n][ind_comb].flux[m] = 0.0; + // start by setting up the time and frequency bins. + // it is all done interms of a logarithmic spacing in both t and nu - get the + // step sizes first. + for (int n = 0; n < VMTBINS; n++) { + for (int ind_comb = 0; ind_comb < indexmax; ind_comb++) { + vspecpol[n][ind_comb].lower_time = exp(log(VSPEC_TIMEMIN) + (n * (dlogt_vspec))); + vspecpol[n][ind_comb].delta_t = + exp(log(VSPEC_TIMEMIN) + ((n + 1) * (dlogt_vspec))) - vspecpol[n][ind_comb].lower_time; + + for (auto &flux : vspecpol[n][ind_comb].flux) { + flux.i = 0.0; + flux.q = 0.0; + flux.u = 0.0; } } } } -void write_vspecpol(FILE *specpol_file) { +static void write_vspecpol(FILE *specpol_file) { for (int ind_comb = 0; ind_comb < (Nobs * Nspectra); ind_comb++) { fprintf(specpol_file, "%g ", 0.); for (int l = 0; l < 3; l++) { for (int p = 0; p < VMTBINS; p++) { - fprintf(specpol_file, "%g ", (vstokes_i[p][ind_comb].lower_time + (vstokes_i[p][ind_comb].delta_t / 2.)) / DAY); + fprintf(specpol_file, "%g ", (vspecpol[p][ind_comb].lower_time + (vspecpol[p][ind_comb].delta_t / 2.)) / DAY); } } @@ -501,17 +491,17 @@ void write_vspecpol(FILE *specpol_file) { // Stokes I for (int p = 0; p < VMTBINS; p++) { - fprintf(specpol_file, "%g ", vstokes_i[p][ind_comb].flux[m]); + fprintf(specpol_file, "%g ", vspecpol[p][ind_comb].flux[m].i); } // Stokes Q for (int p = 0; p < VMTBINS; p++) { - fprintf(specpol_file, "%g ", vstokes_q[p][ind_comb].flux[m]); + fprintf(specpol_file, "%g ", vspecpol[p][ind_comb].flux[m].q); } // Stokes U for (int p = 0; p < VMTBINS; p++) { - fprintf(specpol_file, "%g ", vstokes_u[p][ind_comb].flux[m]); + fprintf(specpol_file, "%g ", vspecpol[p][ind_comb].flux[m].u); } fprintf(specpol_file, "\n"); @@ -519,38 +509,20 @@ void write_vspecpol(FILE *specpol_file) { } } -void read_vspecpol(int my_rank, int nts) { +static void read_vspecpol(int my_rank, int nts) { char filename[MAXFILENAMELENGTH]; - if (nts % 2 == 0) { - snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_odd.tmp", 0, my_rank); - } else { - snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_even.tmp", 0, my_rank); - } + snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_ts%d.tmp", 0, my_rank, nts); + printout("Reading vspecpol file %s\n", filename); - FILE *vspecpol_file = fopen_required(filename, "rb"); + FILE *vspecpol_file = fopen_required(filename, "r"); float a = NAN; float b = NAN; float c = NAN; for (int ind_comb = 0; ind_comb < (Nobs * Nspectra); ind_comb++) { - // Initialise times and frequencies - dlogt_vspec = (log(tmax_vspec) - log(tmin_vspec)) / VMTBINS; - dlognu_vspec = (log(numax_vspec) - log(numin_vspec)) / VMNUBINS; - - for (int n = 0; n < VMTBINS; n++) { - vstokes_i[n][ind_comb].lower_time = exp(log(tmin_vspec) + (n * (dlogt_vspec))); - vstokes_i[n][ind_comb].delta_t = - exp(log(tmin_vspec) + ((n + 1) * (dlogt_vspec))) - vstokes_i[n][ind_comb].lower_time; - - for (int m = 0; m < VMNUBINS; m++) { - lower_freq_vspec[m] = exp(log(numin_vspec) + (m * (dlognu_vspec))); - delta_freq_vspec[m] = exp(log(numin_vspec) + ((m + 1) * (dlognu_vspec))) - lower_freq_vspec[m]; - } - } - - // Initialise I,Q,U fluxes (from temporary files) + // Initialise I,Q,U fluxes from temporary files assert_always(fscanf(vspecpol_file, "%g ", &a) == 1); for (int l = 0; l < 3; l++) { @@ -566,17 +538,17 @@ void read_vspecpol(int my_rank, int nts) { // Stokes I for (int p = 0; p < VMTBINS; p++) { - assert_always(fscanf(vspecpol_file, "%lg ", &vstokes_i[p][ind_comb].flux[j]) == 1); + assert_always(fscanf(vspecpol_file, "%lg ", &vspecpol[p][ind_comb].flux[j].i) == 1); } // Stokes Q for (int p = 0; p < VMTBINS; p++) { - assert_always(fscanf(vspecpol_file, "%lg ", &vstokes_q[p][ind_comb].flux[j]) == 1); + assert_always(fscanf(vspecpol_file, "%lg ", &vspecpol[p][ind_comb].flux[j].q) == 1); } // Stokes U for (int p = 0; p < VMTBINS; p++) { - assert_always(fscanf(vspecpol_file, "%lg ", &vstokes_u[p][ind_comb].flux[j]) == 1); + assert_always(fscanf(vspecpol_file, "%lg ", &vspecpol[p][ind_comb].flux[j].u) == 1); } assert_always(fscanf(vspecpol_file, "\n") == 0); @@ -586,102 +558,47 @@ void read_vspecpol(int my_rank, int nts) { fclose(vspecpol_file); } -void init_vpkt_grid() { - const double ybin = 2 * globals::vmax / NY_VGRID; - const double zbin = 2 * globals::vmax / NZ_VGRID; - - for (int n = 0; n < NY_VGRID; n++) { - for (int m = 0; m < NZ_VGRID; m++) { - for (int bin_range = 0; bin_range < MRANGE_GRID; bin_range++) { - vgrid_i[n][m].flux[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_i[n][m].yvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_i[n][m].zvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - - vgrid_q[n][m].flux[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_q[n][m].yvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_q[n][m].zvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - - vgrid_u[n][m].flux[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_u[n][m].yvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - vgrid_u[n][m].zvel[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - - vgrid_i[n][m].flux[bin_range] = static_cast(malloc(Nobs * sizeof(double))); - for (int bin = 0; bin < Nobs; bin++) { - vgrid_i[n][m].flux[bin_range][bin] = 0.0; - vgrid_q[n][m].flux[bin_range][bin] = 0.0; - vgrid_u[n][m].flux[bin_range][bin] = 0.0; - - vgrid_i[n][m].yvel[bin_range][bin] = globals::vmax - (n + 0.5) * ybin; - vgrid_i[n][m].zvel[bin_range][bin] = globals::vmax - (m + 0.5) * zbin; - } - } - } - } -} - -// Routine to add a packet to the outcoming spectrum. -void add_to_vpkt_grid(struct packet *dummy_ptr, const double *vel, int bin_range, int bin, const double *obs) { - double vref1 = NAN; - double vref2 = NAN; - - // Observer orientation +static void init_vpkt_grid() { + const double ybin = 2 * globals::vmax / VGRID_NY; + const double zbin = 2 * globals::vmax / VGRID_NZ; - const double nx = obs[0]; - const double ny = obs[1]; - const double nz = obs[2]; + for (int n = 0; n < VGRID_NY; n++) { + for (int m = 0; m < VGRID_NZ; m++) { + const double yvel = globals::vmax - (n + 0.5) * ybin; + const double zvel = globals::vmax - (m + 0.5) * zbin; - // Packet velocity + vgrid_i[n][m].yvel = yvel; + vgrid_i[n][m].zvel = zvel; - /* if nobs = x , vref1 = vy and vref2 = vz */ - if (nx == 1) { - vref1 = vel[1]; - vref2 = vel[2]; - } - /* if nobs = x , vref1 = vy and vref2 = vz */ - else if (nx == -1) { - vref1 = -vel[1]; - vref2 = -vel[2]; - } + vgrid_q[n][m].yvel = yvel; + vgrid_q[n][m].zvel = zvel; - // Rotate velocity into projected area seen by the observer (see notes) - else { - // Rotate velocity from (x,y,z) to (n_obs,ref1,ref2) so that x correspond to n_obs (see notes) - vref1 = -ny * vel[0] + (nx + nz * nz / (1 + nx)) * vel[1] - ny * nz * (1 - nx) / sqrt(1 - nx * nx) * vel[2]; - vref2 = -nz * vel[0] - ny * nz * (1 - nx) / sqrt(1 - nx * nx) * vel[1] + (nx + ny * ny / (1 + nx)) * vel[2]; - } + vgrid_u[n][m].yvel = yvel; + vgrid_u[n][m].zvel = zvel; - // Outside the grid - if (fabs(vref1) >= globals::vmax || fabs(vref2) >= globals::vmax) { - return; - } - - // Bin size - const double ybin = 2 * globals::vmax / NY_VGRID; - const double zbin = 2 * globals::vmax / NZ_VGRID; - - // Grid cell - const int nt = static_cast((globals::vmax - vref1) / ybin); - const int mt = static_cast((globals::vmax - vref2) / zbin); - - // Add contribution - if (dummy_ptr->nu_rf > nu_grid_min[bin_range] && dummy_ptr->nu_rf < nu_grid_max[bin_range]) { - safeadd(vgrid_i[nt][mt].flux[bin_range][bin], dummy_ptr->stokes[0] * dummy_ptr->e_rf); - safeadd(vgrid_q[nt][mt].flux[bin_range][bin], dummy_ptr->stokes[1] * dummy_ptr->e_rf); - safeadd(vgrid_u[nt][mt].flux[bin_range][bin], dummy_ptr->stokes[2] * dummy_ptr->e_rf); + vgrid_i[n][m].flux.resize(Nrange_grid, {}); + vgrid_q[n][m].flux.resize(Nrange_grid, {}); + vgrid_u[n][m].flux.resize(Nrange_grid, {}); + for (int wlbin = 0; wlbin < Nrange_grid; wlbin++) { + vgrid_i[n][m].flux[wlbin] = std::vector(Nobs, 0.); + vgrid_q[n][m].flux[wlbin] = std::vector(Nobs, 0.); + vgrid_u[n][m].flux[wlbin] = std::vector(Nobs, 0.); + } + } } } -void write_vpkt_grid(FILE *vpkt_grid_file) { - for (int bin = 0; bin < Nobs; bin++) { - for (int bin_range = 0; bin_range < Nrange_grid; bin_range++) { - for (int n = 0; n < NY_VGRID; n++) { - for (int m = 0; m < NZ_VGRID; m++) { - fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].yvel[bin_range][bin]); - fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].zvel[bin_range][bin]); +static void write_vpkt_grid(FILE *vpkt_grid_file) { + for (int obsbin = 0; obsbin < Nobs; obsbin++) { + for (int wlbin = 0; wlbin < Nrange_grid; wlbin++) { + for (int n = 0; n < VGRID_NY; n++) { + for (int m = 0; m < VGRID_NZ; m++) { + fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].yvel); + fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].zvel); - fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].flux[bin_range][bin]); - fprintf(vpkt_grid_file, "%g ", vgrid_q[n][m].flux[bin_range][bin]); - fprintf(vpkt_grid_file, "%g ", vgrid_u[n][m].flux[bin_range][bin]); + fprintf(vpkt_grid_file, "%g ", vgrid_i[n][m].flux[wlbin][obsbin]); + fprintf(vpkt_grid_file, "%g ", vgrid_q[n][m].flux[wlbin][obsbin]); + fprintf(vpkt_grid_file, "%g ", vgrid_u[n][m].flux[wlbin][obsbin]); fprintf(vpkt_grid_file, "\n"); } @@ -690,23 +607,47 @@ void write_vpkt_grid(FILE *vpkt_grid_file) { } } -void read_vpkt_grid(FILE *vpkt_grid_file) { - for (int bin = 0; bin < Nobs; bin++) { - for (int bin_range = 0; bin_range < Nrange_grid; bin_range++) { - for (int n = 0; n < NY_VGRID; n++) { - for (int m = 0; m < NZ_VGRID; m++) { - assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].yvel[bin_range][bin]) == 1); - assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].zvel[bin_range][bin]) == 1); +static void read_vpkt_grid(const int my_rank, const int nts) { + if (!vgrid_on) { + return; + } + + char filename[MAXFILENAMELENGTH]; + snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_ts%d.tmp", 0, my_rank, nts); + printout("Reading vpkt grid file %s\n", filename); + FILE *vpkt_grid_file = fopen_required(filename, "r"); + + for (int obsbin = 0; obsbin < Nobs; obsbin++) { + for (int wlbin = 0; wlbin < Nrange_grid; wlbin++) { + for (int n = 0; n < VGRID_NY; n++) { + for (int m = 0; m < VGRID_NZ; m++) { + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].yvel) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].zvel) == 1); - assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].flux[bin_range][bin]) == 1); - assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_q[n][m].flux[bin_range][bin]) == 1); - assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_u[n][m].flux[bin_range][bin]) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_i[n][m].flux[wlbin][obsbin]) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_q[n][m].flux[wlbin][obsbin]) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_u[n][m].flux[wlbin][obsbin]) == 1); assert_always(fscanf(vpkt_grid_file, "\n") == 0); } } } } + + fclose(vpkt_grid_file); +} + +void vpkt_remove_temp_file(const int nts, const int my_rank) { + char filenames[2][MAXFILENAMELENGTH]; + snprintf(filenames[0], MAXFILENAMELENGTH, "vspecpol_%d_%d_ts%d.tmp", 0, my_rank, nts); + snprintf(filenames[1], MAXFILENAMELENGTH, "vpkt_grid_%d_%d_ts%d.tmp", 0, my_rank, nts); + + for (auto &filename : filenames) { + if (access(filename, F_OK) == 0) { + remove(filename); + printout("Deleted %s\n", filename); + } + } } void read_parameterfile_vpkt() { @@ -718,7 +659,7 @@ void read_parameterfile_vpkt() { printout("vpkt.txt: Nobs %d directions\n", Nobs); // nz_obs_vpkt. Cos(theta) to the observer. A list in the case of many observers - nz_obs_vpkt = static_cast(malloc(Nobs * sizeof(double))); + nz_obs_vpkt.resize(Nobs); for (int i = 0; i < Nobs; i++) { assert_always(fscanf(input_file, "%lg", &nz_obs_vpkt[i]) == 1); @@ -733,13 +674,15 @@ void read_parameterfile_vpkt() { } // phi to the observer (degrees). A list in the case of many observers - phiobs = static_cast(malloc(Nobs * sizeof(double))); + phiobs.resize(Nobs); for (int i = 0; i < Nobs; i++) { double phi_degrees = 0.; assert_always(fscanf(input_file, "%lg \n", &phi_degrees) == 1); phiobs[i] = phi_degrees * PI / 180.; + const double theta_degrees = std::acos(nz_obs_vpkt[i]) / PI * 180.; - printout("vpkt.txt: direction %d costheta %g phi %g (%g degrees)\n", i, nz_obs_vpkt[i], phiobs[i], phi_degrees); + printout("vpkt.txt: direction %d costheta %g (%.1f degrees) phi %g (%.1f degrees)\n", i, nz_obs_vpkt[i], + theta_degrees, phiobs[i], phi_degrees); } // Nspectra opacity choices (i.e. Nspectra spectra for each observer) @@ -748,15 +691,15 @@ void read_parameterfile_vpkt() { if (nspectra_customlist_flag != 1) { Nspectra = 1; - exclude = static_cast(malloc(Nspectra * sizeof(double))); + exclude.resize(Nspectra, 0); exclude[0] = 0; } else { assert_always(fscanf(input_file, "%d ", &Nspectra) == 1); - exclude = static_cast(malloc(Nspectra * sizeof(double))); + exclude.resize(Nspectra, 0); for (int i = 0; i < Nspectra; i++) { - assert_always(fscanf(input_file, "%lg ", &exclude[i]) == 1); + assert_always(fscanf(input_file, "%d ", &exclude[i]) == 1); // The first number should be equal to zero! assert_always(exclude[0] == 0); // The first spectrum should allow for all opacities (exclude[i]=0) @@ -764,7 +707,7 @@ void read_parameterfile_vpkt() { } printout("vpkt.txt: Nspectra %d per observer\n", Nspectra); - tau_vpkt = static_cast(malloc(Nspectra * sizeof(double))); + tau_vpkt.resize(Nspectra, 0.); // time window. If dum4=1 it restrict vpkt to time windown (dum5,dum6) int override_tminmax = 0; @@ -772,22 +715,24 @@ void read_parameterfile_vpkt() { double vspec_tmax_in_days = 0.; assert_always(fscanf(input_file, "%d %lg %lg \n", &override_tminmax, &vspec_tmin_in_days, &vspec_tmax_in_days) == 3); - printout("vpkt: compiled with tmin_vspec %.1fd tmax_vspec %1.fd VMTBINS %d\n", tmin_vspec / DAY, tmax_vspec / DAY, - VMTBINS); + printout("vpkt: compiled with VSPEC_TIMEMIN %.1fd VSPEC_TIMEMAX %1.fd VMTBINS %d\n", VSPEC_TIMEMIN / DAY, + VSPEC_TIMEMAX / DAY, VMTBINS); if (override_tminmax == 1) { - tmin_vspec_input = vspec_tmin_in_days * DAY; - tmax_vspec_input = vspec_tmax_in_days * DAY; - printout("vpkt.txt: tmin_vspec_input %.1fd, tmax_vspec_input %.1fd\n", tmin_vspec_input / DAY, - tmax_vspec_input / DAY); + VSPEC_TIMEMIN_input = vspec_tmin_in_days * DAY; + VSPEC_TIMEMAX_input = vspec_tmax_in_days * DAY; + printout("vpkt.txt: VSPEC_TIMEMIN_input %.1fd, VSPEC_TIMEMAX_input %.1fd\n", VSPEC_TIMEMIN_input / DAY, + VSPEC_TIMEMAX_input / DAY); } else { - tmin_vspec_input = tmin_vspec; - tmax_vspec_input = tmax_vspec; - printout("vpkt.txt: tmin_vspec_input %.1fd, tmax_vspec_input %.1fd (inherited from tmin_vspec and tmax_vspec)\n", - tmin_vspec_input / DAY, tmax_vspec_input / DAY); + VSPEC_TIMEMIN_input = VSPEC_TIMEMIN; + VSPEC_TIMEMAX_input = VSPEC_TIMEMAX; + printout( + "vpkt.txt: VSPEC_TIMEMIN_input %.1fd, VSPEC_TIMEMAX_input %.1fd (inherited from VSPEC_TIMEMIN and " + "VSPEC_TIMEMAX)\n", + VSPEC_TIMEMIN_input / DAY, VSPEC_TIMEMAX_input / DAY); } - assert_always(tmin_vspec_input >= tmin_vspec); - assert_always(tmax_vspec_input <= tmax_vspec); + assert_always(VSPEC_TIMEMIN_input >= VSPEC_TIMEMIN); + assert_always(VSPEC_TIMEMAX_input <= VSPEC_TIMEMAX); // frequency window. dum4 restrict vpkt to a frequency range, dum5 indicates the number of ranges, // followed by a list of ranges (dum6,dum7) @@ -795,12 +740,14 @@ void read_parameterfile_vpkt() { assert_always(fscanf(input_file, "%d ", &flag_custom_freq_ranges) == 1); printout("vpkt: compiled with VMNUBINS %d\n", VMNUBINS); - assert_always(numax_vspec > numin_vspec); - printout("vpkt: compiled with numax_vspec %g lambda_min %g Å\n", numax_vspec, 1e8 * CLIGHT / numax_vspec); - printout("vpkt: compiled with numin_vspec %g lambda_max %g Å\n", numin_vspec, 1e8 * CLIGHT / numin_vspec); + assert_always(VSPEC_NUMAX > VSPEC_NUMIN); + printout("vpkt: compiled with VSPEC_NUMAX %g lambda_min %g Å\n", VSPEC_NUMAX, 1e8 * CLIGHT / VSPEC_NUMAX); + printout("vpkt: compiled with VSPEC_NUMIN %g lambda_max %g Å\n", VSPEC_NUMIN, 1e8 * CLIGHT / VSPEC_NUMIN); + if (flag_custom_freq_ranges == 1) { assert_always(fscanf(input_file, "%d ", &Nrange) == 1); - assert_always(Nrange <= MRANGE); + VSPEC_NUMIN_input.resize(Nrange, 0.); + VSPEC_NUMAX_input.resize(Nrange, 0.); printout("vpkt.txt: Nrange %d frequency intervals per spectrum per observer\n", Nrange); @@ -809,21 +756,21 @@ void read_parameterfile_vpkt() { double lmax_vspec_input = 0.; assert_always(fscanf(input_file, "%lg %lg", &lmin_vspec_input, &lmax_vspec_input) == 2); - numin_vspec_input[i] = CLIGHT / (lmax_vspec_input * 1e-8); - numax_vspec_input[i] = CLIGHT / (lmin_vspec_input * 1e-8); - printout("vpkt.txt: range %d lambda [%g, %g] Angstroms\n", i, 1e8 * CLIGHT / numax_vspec_input[i], - 1e8 * CLIGHT / numin_vspec_input[i]); + VSPEC_NUMIN_input[i] = CLIGHT / (lmax_vspec_input * 1e-8); + VSPEC_NUMAX_input[i] = CLIGHT / (lmin_vspec_input * 1e-8); } } else { Nrange = 1; - numin_vspec_input[0] = numin_vspec; - numax_vspec_input[0] = numax_vspec; + VSPEC_NUMIN_input.push_back(VSPEC_NUMIN); + VSPEC_NUMAX_input.push_back(VSPEC_NUMAX); - printout("vpkt.txt: Nrange 1 frequency interval (inherited from numin_vspec and numax_vspec)\n"); - const int i = 0; - printout("vpkt.txt: range %d lambda [%g, %g] Angstroms\n", i, 1e8 * CLIGHT / numax_vspec_input[i], - 1e8 * CLIGHT / numin_vspec_input[i]); + printout("vpkt.txt: Nrange 1 frequency interval (inherited from VSPEC_NUMIN and VSPEC_NUMAX)\n"); + } + + for (int i = 0; i < Nrange; i++) { + printout("vpkt.txt: range %d lambda [%g, %g] Angstroms\n", i, 1e8 * CLIGHT / VSPEC_NUMAX_input[i], + 1e8 * CLIGHT / VSPEC_NUMIN_input[i]); } // if dum7=1, vpkt are not created when cell optical depth is larger than cell_is_optically_thick_vpkt @@ -843,10 +790,12 @@ void read_parameterfile_vpkt() { printout("vpkt.txt: tau_max_vpkt %g\n", tau_max_vpkt); // Produce velocity grid map if =1 - assert_always(fscanf(input_file, "%d \n", &vgrid_flag) == 1); - printout("vpkt.txt: velocity grid map %s\n", (vgrid_flag == 1) ? "ENABLED" : "DISABLED"); + int in_vgrid_on = 0; + assert_always(fscanf(input_file, "%d \n", &in_vgrid_on) == 1); + vgrid_on = in_vgrid_on != 0; + printout("vpkt.txt: velocity grid map %s\n", (vgrid_on) ? "ENABLED" : "DISABLED"); - if (vgrid_flag == 1) { + if (vgrid_on) { double tmin_grid_in_days = NAN; double tmax_grid_in_days = NAN; // Specify time range for velocity grid map @@ -861,8 +810,8 @@ void read_parameterfile_vpkt() { printout("vpkt.txt: velocity grid frequency intervals %d\n", Nrange_grid); - assert_always(Nrange_grid <= MRANGE_GRID); - + nu_grid_max.resize(Nrange_grid, 0.); + nu_grid_min.resize(Nrange_grid, 0.); for (int i = 0; i < Nrange_grid; i++) { double range_lambda_min = 0.; double range_lambda_max = 0.; @@ -879,22 +828,78 @@ void read_parameterfile_vpkt() { fclose(input_file); } -auto vpkt_call_estimators(struct packet *pkt_ptr, double t_current, int realtype) -> int { - double obs[3]; - int vflag = 0; +void vpkt_write_timestep(const int nts, const int my_rank, const int tid, + const bool is_final) { // write specpol of the virtual packets + if constexpr (!VPKT_ON) { + return; + } - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, t_current); + char filename[MAXFILENAMELENGTH]; + + if (is_final) { + snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d-%d.out", my_rank, tid); + } else { + snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_ts%d.tmp", 0, my_rank, nts); + } + + printout("Writing vspecpol file %s\n", filename); + FILE *vspecpol_file = fopen_required(filename, "w"); + write_vspecpol(vspecpol_file); + fclose(vspecpol_file); + + if (vgrid_on) { + if (is_final) { + snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d-%d.out", my_rank, tid); + } else { + snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_ts%d.tmp", 0, my_rank, nts); + } + + printout("Writing vpkt grid file %s\n", filename); + FILE *vpkt_grid_file = fopen_required(filename, "w"); + write_vpkt_grid(vpkt_grid_file); + fclose(vpkt_grid_file); + } +} + +void vpkt_init(const int nts, const int my_rank, const int tid, const bool continued_from_saved) { + if constexpr (!VPKT_ON) { + return; + } + + init_vspecpol(); + if (vgrid_on) { + init_vpkt_grid(); + } + + if (continued_from_saved) { + // Continue simulation: read into temporary files + + read_vspecpol(my_rank, nts); + + if (vgrid_on) { + read_vpkt_grid(my_rank, nts); + } + } +} + +auto vpkt_call_estimators(struct packet *pkt_ptr, const enum packet_type realtype) -> void { + if constexpr (!VPKT_ON) { + return; + } // Cut on vpkts - int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); + const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); if (grid::modelgrid[mgi].thick != 0) { - return 0; + return; } - /* this is just to find the next_trans value when is set to 0 (avoid doing that in the vpkt routine for each observer) - */ + const double t_current = pkt_ptr->prop_time; + + double vel_vec[3]; + get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); + + // this is just to find the next_trans value when is set to 0 (avoid doing that in the vpkt routine for each observer) if (pkt_ptr->next_trans == 0) { const int lineindex = closest_transition(pkt_ptr->nu_cmf, pkt_ptr->next_trans); /// returns negative if (lineindex < 0) { @@ -902,60 +907,45 @@ auto vpkt_call_estimators(struct packet *pkt_ptr, double t_current, int realtype } } - for (int bin = 0; bin < Nobs; bin++) { - /* loop over different observers */ + for (int obsbin = 0; obsbin < Nobs; obsbin++) { + // loop over different observer directions - obs[0] = sqrt(1 - nz_obs_vpkt[bin] * nz_obs_vpkt[bin]) * cos(phiobs[bin]); - obs[1] = sqrt(1 - nz_obs_vpkt[bin] * nz_obs_vpkt[bin]) * sin(phiobs[bin]); - obs[2] = nz_obs_vpkt[bin]; + double obsdir[3] = {sqrt(1 - nz_obs_vpkt[obsbin] * nz_obs_vpkt[obsbin]) * cos(phiobs[obsbin]), + sqrt(1 - nz_obs_vpkt[obsbin] * nz_obs_vpkt[obsbin]) * sin(phiobs[obsbin]), nz_obs_vpkt[obsbin]}; - const double t_arrive = t_current - (dot(pkt_ptr->pos, obs) / CLIGHT_PROP); + const double t_arrive = t_current - (dot(pkt_ptr->pos, obsdir) / CLIGHT_PROP); - if (t_arrive >= tmin_vspec_input && t_arrive <= tmax_vspec_input) { + if (t_arrive >= VSPEC_TIMEMIN_input && t_arrive <= VSPEC_TIMEMAX_input) { // time selection + const double nu_rf = pkt_ptr->nu_cmf / doppler_nucmf_on_nurf(obsdir, vel_vec); + for (int i = 0; i < Nrange; i++) { // Loop over frequency ranges - if (pkt_ptr->nu_cmf / doppler_nucmf_on_nurf(obs, vel_vec) > numin_vspec_input[i] && - pkt_ptr->nu_cmf / doppler_nucmf_on_nurf(obs, vel_vec) < numax_vspec_input[i]) { + if (nu_rf > VSPEC_NUMIN_input[i] && nu_rf < VSPEC_NUMAX_input[i]) { // frequency selection - rlc_emiss_vpkt(pkt_ptr, t_current, bin, obs, realtype); - - vflag = 1; - - // Need to update the starting cell for next observer - // If previous vpkt reached tau_lim, change_cell (and then update_cell) hasn't been called - mgi = grid::get_cell_modelgridindex(pkt_ptr->where); - cellhistory_reset(mgi, false); + rlc_emiss_vpkt(pkt_ptr, t_current, obsbin, obsdir, realtype); } } } } - - // we just used the opacity variables for v-packets. We need to reset them for the original r packet - calculate_kappa_rpkt_cont(pkt_ptr, &globals::kappa_rpkt_cont[tid]); - - return vflag; } -auto rot_angle(double *n1, double *n2, double *ref1, double *ref2) -> double { - /* ------------- Rotation angle from the scattering plane --------------------------------------------- */ - /* -------- We need to rotate Stokes Parameters to (or from) the scattering plane from (or to) -------- */ - /* -------- the meridian frame such that Q=1 is in the scattering plane and along ref1 ---------------- */ - - double i = 0; +auto rot_angle(std::span n1, std::span n2, std::span ref1, std::span ref2) + -> double { + // Rotation angle from the scattering plane + // We need to rotate Stokes Parameters to (or from) the scattering plane from (or to) + // the meridian frame such that Q=1 is in the scattering plane and along ref1 - double ref1_sc[3]; // ref1_sc is the ref1 axis in the scattering plane ref1 = n1 x ( n1 x n2 ) - ref1_sc[0] = n1[0] * dot(n1, n2) - n2[0]; - ref1_sc[1] = n1[1] * dot(n1, n2) - n2[1]; - ref1_sc[2] = n1[2] * dot(n1, n2) - n2[2]; + const double n1_dot_n2 = dot(n1, n2); + double ref1_sc[3] = {n1[0] * n1_dot_n2 - n2[0], n1[1] * n1_dot_n2 - n2[1], n1[2] * n1_dot_n2 - n2[2]}; vec_norm(ref1_sc, ref1_sc); double cos_stokes_rot_1 = dot(ref1_sc, ref1); - double const cos_stokes_rot_2 = dot(ref1_sc, ref2); + const double cos_stokes_rot_2 = dot(ref1_sc, ref2); if (cos_stokes_rot_1 < -1) { cos_stokes_rot_1 = -1; @@ -964,20 +954,18 @@ auto rot_angle(double *n1, double *n2, double *ref1, double *ref2) -> double { cos_stokes_rot_1 = 1; } + double i = 0; if ((cos_stokes_rot_1 > 0) && (cos_stokes_rot_2 > 0)) { i = acos(cos_stokes_rot_1); - } - if ((cos_stokes_rot_1 > 0) && (cos_stokes_rot_2 < 0)) { - i = 2 * acos(-1.) - acos(cos_stokes_rot_1); - } - if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 < 0)) { - i = acos(-1.) + acos(fabs(cos_stokes_rot_1)); - } - if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 > 0)) { - i = acos(-1.) - acos(fabs(cos_stokes_rot_1)); + } else if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 > 0)) { + i = M_PI - acos(fabs(cos_stokes_rot_1)); + } else if ((cos_stokes_rot_1 > 0) && (cos_stokes_rot_2 < 0)) { + i = 2 * M_PI - acos(cos_stokes_rot_1); + } else if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 < 0)) { + i = M_PI + acos(fabs(cos_stokes_rot_1)); } if (cos_stokes_rot_1 == 0) { - i = acos(-1.) / 2.; + i = M_PI / 2.; } if (cos_stokes_rot_2 == 0) { i = 0.0; @@ -991,32 +979,62 @@ auto rot_angle(double *n1, double *n2, double *ref1, double *ref2) -> double { } // Routine to compute the meridian frame axes ref1 and ref2 -void meridian(const double *n, double *ref1, double *ref2) { +void meridian(std::span n, std::span ref1, std::span ref2) { // for ref_1 use (from triple product rule) - - ref1[0] = -1. * n[0] * n[2] / sqrt(n[0] * n[0] + n[1] * n[1]); - ref1[1] = -1. * n[1] * n[2] / sqrt(n[0] * n[0] + n[1] * n[1]); - ref1[2] = (1 - (n[2] * n[2])) / sqrt(n[0] * n[0] + n[1] * n[1]); + const double n_xylen = sqrt(n[0] * n[0] + n[1] * n[1]); + ref1[0] = -1. * n[0] * n[2] / n_xylen; + ref1[1] = -1. * n[1] * n[2] / n_xylen; + ref1[2] = (1 - (n[2] * n[2])) / n_xylen; // for ref_2 use vector product of n_cmf with ref1 + cross_prod(ref1, n, ref2); +} + +static void lorentz(std::span e_rf, std::span n_rf, std::span v, + std::span e_cmf) { + // Lorentz transformations from RF to CMF + + std::array beta = {v[0] / CLIGHT, v[1] / CLIGHT, v[2] / CLIGHT}; + const double vsqr = dot(beta, beta); + + const double gamma_rel = 1. / (sqrt(1 - vsqr)); + + std::array e_par = {dot(e_rf, beta) * beta[0] / (vsqr), dot(e_rf, beta) * beta[1] / (vsqr), + dot(e_rf, beta) * beta[2] / (vsqr)}; + + std::array e_perp = {e_rf[0] - e_par[0], e_rf[1] - e_par[1], e_rf[2] - e_par[2]}; - ref2[0] = n[2] * ref1[1] - n[1] * ref1[2]; - ref2[1] = n[0] * ref1[2] - n[2] * ref1[0]; - ref2[2] = n[1] * ref1[0] - n[0] * ref1[1]; + std::array b_rf = {NAN, NAN, NAN}; + cross_prod(n_rf, e_rf, b_rf); + + // const double b_par[3] = {dot(b_rf, beta) * beta[0] / (vsqr), dot(b_rf, beta) * beta[1] / (vsqr), + // dot(b_rf, beta) * beta[2] / (vsqr)}; + + // const double b_perp[3] = {b_rf[0] - b_par[0], b_rf[1] - b_par[1], b_rf[2] - b_par[2]}; + + std::array v_cr_b = {NAN, NAN, NAN}; + cross_prod(beta, b_rf, v_cr_b); + + // const double v_cr_e[3] = {beta[1] * e_rf[2] - beta[2] * e_rf[1], beta[2] * e_rf[0] - beta[0] * e_rf[2], + // beta[0] * e_rf[1] - beta[1] * e_rf[0]}; + + e_cmf[0] = e_par[0] + gamma_rel * (e_perp[0] + v_cr_b[0]); + e_cmf[1] = e_par[1] + gamma_rel * (e_perp[1] + v_cr_b[1]); + e_cmf[2] = e_par[2] + gamma_rel * (e_perp[2] + v_cr_b[2]); + vec_norm(e_cmf, e_cmf); + + // double b_cmf[3]; + // b_cmf[0] = b_par[0] + gamma_rel * (b_perp[0] - v_cr_e[0]); + // b_cmf[1] = b_par[1] + gamma_rel * (b_perp[1] - v_cr_e[1]); + // b_cmf[2] = b_par[2] + gamma_rel * (b_perp[2] - v_cr_e[2]); + // vec_norm(b_cmf, b_cmf); } // Routine to transform the Stokes Parameters from RF to CMF -void frame_transform(double *n_rf, double *Q, double *U, double *v, double *n_cmf) { - double cos2rot_angle = NAN; - double sin2rot_angle = NAN; - double e_rf[3]; - double e_cmf[3]; - double e_cmf_ref1 = 0.; - double e_cmf_ref2 = 0.; - double theta_rot = 0.; - - double ref1[3]; - double ref2[3]; +void frame_transform(std::span n_rf, double *Q, double *U, std::span v, + std::span n_cmf) { + double ref1[3] = {NAN, NAN, NAN}; + double ref2[3] = {NAN, NAN, NAN}; // Meridian frame in the RF meridian(n_rf, ref1, ref2); @@ -1030,141 +1048,76 @@ void frame_transform(double *n_rf, double *Q, double *U, double *v, double *n_cm double rot_angle = 0; if (p > 0) { - cos2rot_angle = Q0 / p; - sin2rot_angle = U0 / p; + const double cos2rot_angle = Q0 / p; + const double sin2rot_angle = U0 / p; if ((cos2rot_angle > 0) && (sin2rot_angle > 0)) { rot_angle = acos(Q0 / p) / 2.; - } - if ((cos2rot_angle < 0) && (sin2rot_angle > 0)) { - rot_angle = (acos(-1.) - acos(fabs(Q0 / p))) / 2.; - } - if ((cos2rot_angle < 0) && (sin2rot_angle < 0)) { - rot_angle = (acos(-1.) + acos(fabs(Q0 / p))) / 2.; - } - if ((cos2rot_angle > 0) && (sin2rot_angle < 0)) { - rot_angle = (2. * acos(-1.) - acos(fabs(Q0 / p))) / 2.; - } - if (cos2rot_angle == 0) { - rot_angle = 0.25 * acos(-1); + } else if ((cos2rot_angle < 0) && (sin2rot_angle > 0)) { + rot_angle = (M_PI - acos(fabs(cos2rot_angle))) / 2.; + } else if ((cos2rot_angle < 0) && (sin2rot_angle < 0)) { + rot_angle = (M_PI + acos(fabs(cos2rot_angle))) / 2.; + } else if ((cos2rot_angle > 0) && (sin2rot_angle < 0)) { + rot_angle = (2. * M_PI - acos(fabs(cos2rot_angle))) / 2.; + } else if (cos2rot_angle == 0) { + rot_angle = 0.25 * M_PI; if (U0 < 0) { - rot_angle = 0.75 * acos(-1); + rot_angle = 0.75 * M_PI; } } if (sin2rot_angle == 0) { rot_angle = 0.0; if (Q0 < 0) { - rot_angle = 0.5 * acos(-1); + rot_angle = 0.5 * M_PI; } } } // Define electric field by linear combination of ref1 and ref2 (using the angle just computed) - e_rf[0] = cos(rot_angle) * ref1[0] - sin(rot_angle) * ref2[0]; - e_rf[1] = cos(rot_angle) * ref1[1] - sin(rot_angle) * ref2[1]; - e_rf[2] = cos(rot_angle) * ref1[2] - sin(rot_angle) * ref2[2]; + + const double elec_rf[3] = {cos(rot_angle) * ref1[0] - sin(rot_angle) * ref2[0], + cos(rot_angle) * ref1[1] - sin(rot_angle) * ref2[1], + cos(rot_angle) * ref1[2] - sin(rot_angle) * ref2[2]}; // Aberration angle_ab(n_rf, v, n_cmf); + double elec_cmf[3] = {NAN, NAN, NAN}; // Lorentz transformation of E - lorentz(e_rf, n_rf, v, e_cmf); + lorentz(elec_rf, n_rf, v, elec_cmf); // Meridian frame in the CMF meridian(n_cmf, ref1, ref2); // Projection of E onto ref1 and ref2 - e_cmf_ref1 = e_cmf[0] * ref1[0] + e_cmf[1] * ref1[1] + e_cmf[2] * ref1[2]; - e_cmf_ref2 = e_cmf[0] * ref2[0] + e_cmf[1] * ref2[1] + e_cmf[2] * ref2[2]; + const double cosine_elec_ref1 = dot(elec_cmf, ref1); + const double cosine_elec_ref2 = dot(elec_cmf, ref2); // Compute the angle between ref1 and the electric field - if ((e_cmf_ref1 > 0) && (e_cmf_ref2 < 0)) { - theta_rot = acos(e_cmf_ref1); - } - if ((e_cmf_ref1 < 0) && (e_cmf_ref2 < 0)) { - theta_rot = acos(-1.) - acos(fabs(e_cmf_ref1)); - } - if ((e_cmf_ref1 < 0) && (e_cmf_ref2 > 0)) { - theta_rot = acos(-1.) + acos(fabs(e_cmf_ref1)); - } - if ((e_cmf_ref1 > 0) && (e_cmf_ref2 > 0)) { - theta_rot = 2 * acos(-1.) - acos(e_cmf_ref1); + double theta_rot = 0.; + if ((cosine_elec_ref1 > 0) && (cosine_elec_ref2 < 0)) { + theta_rot = acos(cosine_elec_ref1); + } else if ((cosine_elec_ref1 < 0) && (cosine_elec_ref2 > 0)) { + theta_rot = M_PI + acos(fabs(cosine_elec_ref1)); + } else if ((cosine_elec_ref1 < 0) && (cosine_elec_ref2 < 0)) { + theta_rot = M_PI - acos(fabs(cosine_elec_ref1)); + } else if ((cosine_elec_ref1 > 0) && (cosine_elec_ref2 > 0)) { + theta_rot = 2 * M_PI - acos(cosine_elec_ref1); } - if (e_cmf_ref1 == 0) { - theta_rot = acos(-1.) / 2.; + if (cosine_elec_ref1 == 0) { + theta_rot = M_PI / 2.; } - if (e_cmf_ref2 == 0) { + if (cosine_elec_ref2 == 0) { theta_rot = 0.0; } - if (e_cmf_ref1 > 1) { + if (cosine_elec_ref1 > 1) { theta_rot = 0.0; } - if (e_cmf_ref1 < -1) { - theta_rot = acos(-1.); + if (cosine_elec_ref1 < -1) { + theta_rot = M_PI; } // Compute Stokes Parameters in the CMF *Q = cos(2 * theta_rot) * p; *U = sin(2 * theta_rot) * p; -} - -/* ----------------------- Lorentz transformations from RF to CMF --------------------------------------------- */ -void lorentz(const double *e_rf, const double *n_rf, const double *v, double *e_cmf) { - double beta[3]; - double e_par[3]; - double e_perp[3]; - double b_rf[3]; - double b_par[3]; - double b_perp[3]; - double vsqr = NAN; - double gamma_rel = NAN; - double v_cr_b[3]; - double v_cr_e[3]; - double b_cmf[3]; - - beta[0] = v[0] / CLIGHT; - beta[1] = v[1] / CLIGHT; - beta[2] = v[2] / CLIGHT; - vsqr = dot(beta, beta); - - gamma_rel = 1. / (sqrt(1 - vsqr)); - - e_par[0] = (e_rf[0] * beta[0] + e_rf[1] * beta[1] + e_rf[2] * beta[2]) * beta[0] / (vsqr); - e_par[1] = (e_rf[0] * beta[0] + e_rf[1] * beta[1] + e_rf[2] * beta[2]) * beta[1] / (vsqr); - e_par[2] = (e_rf[0] * beta[0] + e_rf[1] * beta[1] + e_rf[2] * beta[2]) * beta[2] / (vsqr); - - e_perp[0] = e_rf[0] - e_par[0]; - e_perp[1] = e_rf[1] - e_par[1]; - e_perp[2] = e_rf[2] - e_par[2]; - - b_rf[0] = n_rf[1] * e_rf[2] - n_rf[2] * e_rf[1]; - b_rf[1] = n_rf[2] * e_rf[0] - n_rf[0] * e_rf[2]; - b_rf[2] = n_rf[0] * e_rf[1] - n_rf[1] * e_rf[0]; - - b_par[0] = (b_rf[0] * beta[0] + b_rf[1] * beta[1] + b_rf[2] * beta[2]) * beta[0] / (vsqr); - b_par[1] = (b_rf[0] * beta[0] + b_rf[1] * beta[1] + b_rf[2] * beta[2]) * beta[1] / (vsqr); - b_par[2] = (b_rf[0] * beta[0] + b_rf[1] * beta[1] + b_rf[2] * beta[2]) * beta[2] / (vsqr); - - b_perp[0] = b_rf[0] - b_par[0]; - b_perp[1] = b_rf[1] - b_par[1]; - b_perp[2] = b_rf[2] - b_par[2]; - - v_cr_b[0] = beta[1] * b_rf[2] - beta[2] * b_rf[1]; - v_cr_b[1] = beta[2] * b_rf[0] - beta[0] * b_rf[2]; - v_cr_b[2] = beta[0] * b_rf[1] - beta[1] * b_rf[0]; - - v_cr_e[0] = beta[1] * e_rf[2] - beta[2] * e_rf[1]; - v_cr_e[1] = beta[2] * e_rf[0] - beta[0] * e_rf[2]; - v_cr_e[2] = beta[0] * e_rf[1] - beta[1] * e_rf[0]; - - e_cmf[0] = e_par[0] + gamma_rel * (e_perp[0] + v_cr_b[0]); - e_cmf[1] = e_par[1] + gamma_rel * (e_perp[1] + v_cr_b[1]); - e_cmf[2] = e_par[2] + gamma_rel * (e_perp[2] + v_cr_b[2]); - - b_cmf[0] = b_par[0] + gamma_rel * (b_perp[0] - v_cr_e[0]); - b_cmf[1] = b_par[1] + gamma_rel * (b_perp[1] - v_cr_e[1]); - b_cmf[2] = b_par[2] + gamma_rel * (b_perp[2] - v_cr_e[2]); - - vec_norm(e_cmf, e_cmf); - vec_norm(b_cmf, b_cmf); } \ No newline at end of file diff --git a/vpkt.h b/vpkt.h index 474013b49..dd385d828 100644 --- a/vpkt.h +++ b/vpkt.h @@ -1,51 +1,38 @@ #ifndef VPKT_H #define VPKT_H -#include +#include #include "artisoptions.h" +#include "packet.h" -double rot_angle(double *n1, double *n2, double *ref1, double *ref2); -void meridian(const double *n, double *ref1, double *ref2); -void frame_transform(double *n_rf, double *Q, double *U, double *v, double *n_cmf); -void lorentz(const double *e_rf, const double *n_rf, const double *v, double *e_cmf); +double rot_angle(std::span n1, std::span n2, std::span ref1, + std::span ref2); +void meridian(std::span n, std::span ref1, std::span ref2); +void frame_transform(std::span n_rf, double *Q, double *U, std::span v, + std::span n_cmf); -void rlc_emiss_vpkt(struct packet *pkt_ptr, double t_current, int bin, double *obs, int realtype); -void add_to_vspecpol(struct packet *pkt_ptr, int bin, int ind, double t_arrive); -void init_vspecpol(); void read_parameterfile_vpkt(); -void write_vspecpol(FILE *specpol_file); -void read_vspecpol(int my_rank, int nts); -void init_vpkt_grid(); -void add_to_vpkt_grid(struct packet *dummy_ptr, const double *vel, int bin_range, int bin, const double *obs); -void write_vpkt_grid(FILE *vpkt_grid_file); -void read_vpkt_grid(FILE *vpkt_grid_file); -int check_tau(const double *tau, const double *tau_max); -int vpkt_call_estimators(struct packet *pkt_ptr, double t_current, int realtype); - -// -------------------------------------------------------------------------------- -// --------------------------- VIRTUAL PACKETS ----------------------------------- -// -------------------------------------------------------------------------------- -#define MRANGE_GRID 5 -#define NY_VGRID 50 -#define NZ_VGRID 50 +void vpkt_init(int nts, int my_rank, int tid, bool continued_from_saved); +void vpkt_call_estimators(struct packet *pkt_ptr, const enum packet_type); +void vpkt_write_timestep(int nts, int my_rank, int tid, bool is_final); + +void vpkt_remove_temp_file(int nts, int my_rank); + +constexpr int VGRID_NY = 50; +constexpr int VGRID_NZ = 50; // FREQUENCY // dlognu = (log(numax) - log(numin)) / VMNUBINS ~ 3.9e-4 (10'000 over 1e14-5e15 Hz) -#define numin_vspec (CLIGHT / 10000 * 1e8) -#define numax_vspec (CLIGHT / 3500 * 1e8) -#define VMNUBINS 2500 +constexpr double VSPEC_NUMIN = CLIGHT / 10000 * 1e8; +constexpr double VSPEC_NUMAX = CLIGHT / 3500 * 1e8; +constexpr int VMNUBINS = 2500; // TIME // dlogt = (log(globals::tmin) - log(globals::tmax)) / VMTBINS ~ 3.69e-2 (111 over 2-120 d) -#define tmin_vspec (10 * DAY) -#define tmax_vspec (30 * DAY) -#define VMTBINS 30 - -// Total number of frequency ranges -#define MRANGE 2 - -extern int vgrid_flag; +constexpr double VSPEC_TIMEMIN = 10 * DAY; +constexpr double VSPEC_TIMEMAX = 30 * DAY; +constexpr int VMTBINS = 30; extern int nvpkt; extern int nvpkt_esc1; // electron scattering event