diff --git a/.clang-tidy b/.clang-tidy index 0036c2bb9..c4eb32bac 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -17,7 +17,6 @@ Checks: > -cert-err34-c, -cert-err58-cpp, -clang-analyzer-deadcode.DeadStores, - -clang-diagnostic-unused-macros, -cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-macro-usage, @@ -27,7 +26,6 @@ Checks: > -cppcoreguidelines-pro-bounds-constant-array-index, -cppcoreguidelines-pro-bounds-pointer-arithmetic, -cppcoreguidelines-pro-type-cstyle-cast, - -cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-vararg, -fuchsia-*, -google-objc*, @@ -37,7 +35,6 @@ Checks: > -hicpp-no-array-decay, -hicpp-no-malloc, -hicpp-signed-bitwise, - -llvm-header-guard, -misc-use-anonymous-namespace, -misc-no-recursion, -misc-non-private-member-variables-in-classes, @@ -54,11 +51,17 @@ CheckOptions: value: google - key: cppcoreguidelines-init-variables.MathHeader value: + - key: modernize-use-std-print.PrintfLikeFunctions # replace printout macro with a function when we want to autofix to use std::format gcc >=13 + value: printf; absl::PrintF; printout + - key: modernize-use-std-print.FprintfLikeFunctions + value: fprintf; absl::FPrintF - key: cppcoreguidelines-narrowing-conversions.IgnoreConversionFromTypes - value: size_t;ptrdiff_t;size_t;difference_type;time_t;MPI_Aint + value: size_t;ptrdiff_t;size_type;difference_type;time_t;MPI_Aint - key: cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion value: 'false' - key: cppcoreguidelines-narrowing-conversions.WarnOnIntegerToFloatingPointNarrowingConversion value: 'false' - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays value: 'true' + - key: readability-identifier-naming.NamespaceCase + value: lower_case diff --git a/.github/workflows/ci-checks.yml b/.github/workflows/ci-checks.yml index c7058e23c..eac41a750 100644 --- a/.github/workflows/ci-checks.yml +++ b/.github/workflows/ci-checks.yml @@ -12,7 +12,7 @@ on: jobs: cppcheck: - runs-on: macos-13 + runs-on: macos-14 steps: - uses: actions/checkout@v4 @@ -25,12 +25,12 @@ jobs: - name: run cppcheck and check for errors run: | cppcheck --version - cppcheck --force --error-exitcode=1 --language=c++ --std=c++20 --enable=warning,performance,portability . + cppcheck --force --error-exitcode=1 --language=c++ --std=c++20 --enable=warning,performance,portability --suppress=memleak --check-level=exhaustive . - name: show cppcheck style suggestions run: | cppcheck --version - cppcheck --force --language=c++ --std=c++20 --enable=style --suppress=knownConditionTrueFalse . + cppcheck --force --language=c++ --std=c++20 --enable=style --suppress=knownConditionTrueFalse --check-level=exhaustive . clang-format: runs-on: ubuntu-22.04 @@ -44,7 +44,7 @@ jobs: check-path: . clang-tidy: - runs-on: macos-13 + runs-on: macos-14 steps: - uses: actions/checkout@v4 @@ -61,26 +61,22 @@ jobs: - name: install llvm run: | brew install llvm || true - echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + echo "/opt/homebrew/opt/llvm/bin" >> $GITHUB_PATH - echo "CXX=clang++" >> $GITHUB_ENV - echo "OMPI_CXX=clang++" >> $GITHUB_ENV - - echo "LDFLAGS=-L/usr/local/opt/llvm/lib" >> $GITHUB_ENV - echo "CPPFLAGS=-I/usr/local/opt/llvm/include" >> $GITHUB_ENV + echo "LDFLAGS=-L/opt/homebrew/opt/llvm/lib" >> $GITHUB_ENV + echo "CPPFLAGS=-I/opt/homebrew/opt/llvm/include" >> $GITHUB_ENV - echo "LDFLAGS=-L/usr/local/opt/libomp/lib" >> $GITHUB_ENV - echo "CXXFLAGS=-I/usr/local/opt/libomp/include" >> $GITHUB_ENV + echo "CXXFLAGS=-I/opt/homebrew/opt/libomp/include" >> $GITHUB_ENV - echo "LIBRARY_PATH=/usr/local/opt/lib" >> $GITHUB_ENV - echo "LD_LIBRARY_PATH=/usr/local/opt/lib" >> $GITHUB_ENV + echo "LIBRARY_PATH=/opt/homebrew/opt/lib" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=/opt/homebrew/opt/lib" >> $GITHUB_ENV - echo "CPATH=/usr/local/opt/include" >> $GITHUB_ENV + echo "CPATH=/opt/homebrew/opt/include" >> $GITHUB_ENV echo "compiledb" > requirements.txt - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: cache: pip python-version: '3.12' @@ -106,22 +102,22 @@ jobs: CXX: g++ strategy: matrix: - compiler: - [ + compiler: [ {name: gcc, ver: 11}, {name: gcc, ver: 12}, {name: gcc, ver: 13}, - {name: clang, ver: 14}, - {name: clang, ver: 15}, + # {name: gcc, ver: 14}, + {name: nvc++, ver: '24.3'}, ] mpi: [ON, OFF] openmp: [ON, OFF] + stdpar: [ON, OFF] exclude: - - compiler: {name: clang, ver: 15} - openmp: ON + - openmp: ON + stdpar: ON fail-fast: false - name: ${{ matrix.compiler.name }}-${{ matrix.compiler.ver }}${{ matrix.mpi == 'ON' && ' MPI' || ''}}${{ matrix.openmp == 'ON' && ' OpenMP' || ''}} + name: ${{ matrix.compiler.name }} ${{ matrix.compiler.ver }}${{ matrix.mpi == 'ON' && ' MPI' || ''}}${{ matrix.openmp == 'ON' && ' OpenMP' || ''}}${{ matrix.stdpar == 'ON' && ' stdpar' || ''}} steps: - uses: actions/checkout@v4 @@ -129,28 +125,53 @@ jobs: id: cpu-count run: | g++ -march=native -Q --help=target | grep -- '-march= ' | cut -f3 - # echo "count=$(python3 -c 'import psutil; print(int(psutil.cpu_count(logical=False)))')" >> $GITHUB_OUTPUT + python3 -c 'import multiprocessing; print(f"CPU count: {multiprocessing.cpu_count()}")' echo "count=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')" >> $GITHUB_OUTPUT - - name: apt update + - name: Install gcc + if: matrix.compiler.name == 'gcc' + uses: fortran-lang/setup-fortran@v1 + # continue-on-error: true + with: + compiler: gcc + version: ${{ matrix.compiler.ver }} + + - name: Install gcc for nvc++ + if: matrix.compiler.name == 'nvc++' + uses: fortran-lang/setup-fortran@v1 + continue-on-error: true + with: + compiler: gcc + version: 13 + + # - name: Install cuda-toolkit + # if: matrix.compiler.name == 'nvc++' + # run: | + # sudo apt-get -y install nvidia-cuda-toolkit + # echo "/usr/local/cuda-12.4/bin" >> $GITHUB_PATH + + - name: Install nvc++ + if: matrix.compiler.name == 'nvc++' + uses: fortran-lang/setup-fortran@v1 + # continue-on-error: true + with: + compiler: nvidia-hpc + version: ${{ matrix.compiler.ver }} + + - name: Set nvc++ as compiler + if: matrix.compiler.name == 'nvc++' 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 + echo "CXX=nvc++" >> $GITHUB_ENV + echo "OMPI_CXX=nvc++" >> $GITHUB_ENV - - name: Install gcc-${{ matrix.compiler.ver }} - if: matrix.compiler.name == 'gcc' + - name: install gsl run: | - sudo apt install -y gcc-${{ matrix.compiler.ver }} g++-${{ matrix.compiler.ver }} - echo "CXX=g++-${{ matrix.compiler.ver }}" >> $GITHUB_ENV + sudo apt install libgsl-dev - - name: Install clang-${{ matrix.compiler.ver }} - if: matrix.compiler.name == 'clang' + - name: install tbb + if: matrix.compiler.name == 'gcc' && matrix.stdpar == 'ON' run: | - sudo apt install -y clang-${{ matrix.compiler.ver }} --install-suggests - echo "CXX=clang++-${{ matrix.compiler.ver }}" >> $GITHUB_ENV + sudo apt install libtbb-dev - name: install openmpi if: matrix.mpi == 'ON' @@ -162,30 +183,91 @@ jobs: run: | sudo apt-get install -y libomp5-14 libomp-dev - - name: install gsl - run: sudo apt install libgsl-dev + - name: Compile classic mode + run: | + $CXX -v + cp -v -p artisoptions_classic.h artisoptions.h + make clean + make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} ${{ matrix.stdpar == 'ON' && ' STDPAR=ON' || ''}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec + + - name: Compile nebular mode + run: | + cp -v -p artisoptions_classic.h artisoptions.h + make clean + make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} ${{ matrix.stdpar == 'ON' && ' STDPAR=ON' || ''}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec + + mac-llvm: + runs-on: macos-14 + env: + CXX: clang++ + strategy: + matrix: + mpi: [ON, OFF] + openmp: [ON, OFF] + stdpar: [ON, OFF] + exclude: + - openmp: ON + stdpar: ON + fail-fast: false - # - name: Set compiler environment variables (MPI off) - # if: matrix.mpi == 'OFF' - # run: echo "CXX=${{ matrix.compiler.cmd }}" >> $GITHUB_ENV + name: macOS brew clang ${{ matrix.mpi == 'ON' && ' MPI' || ''}}${{ matrix.openmp == 'ON' && ' OpenMP' || ''}}${{ matrix.stdpar == 'ON' && ' stdpar' || ''}} - - name: Set compiler environment variables (MPI on) + steps: + - uses: actions/checkout@v4 + + - name: CPU core count + id: cpu-count + run: | + # 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 + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 'latest-stable' + + - name: install dependencies + run: | + brew update + brew install gsl || true + + - name: install llvm + run: | + brew install llvm || true + echo "/opt/homebrew/opt/llvm/bin" >> $GITHUB_PATH + echo "LDFLAGS=-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++ $LDFLAGS" >> $GITHUB_ENV + echo "CXX=clang++" >> $GITHUB_ENV + + - name: install OpenMP + if: matrix.openmp == 'ON' + run: | + brew install libomp || true + brew link --force libomp + + echo "LDFLAGS=-L/opt/homebrew/opt/libomp/lib $LDFLAGS" >> $GITHUB_ENV + echo "CXXFLAGS=-I/opt/homebrew/opt/libomp/include $CXXFLAGS" >> $GITHUB_ENV + + - name: install OpenMPI if: matrix.mpi == 'ON' run: | - echo "OMPI_CXX=$CXX" >> $GITHUB_ENV - echo "CXX=mpicxx" >> $GITHUB_ENV - mpicxx --showme:version + brew install openmpi || true - - name: Show compiler version and CPU type + - name: install onedpl and tbb + if: matrix.stdpar == 'ON' run: | - ${{ env.CXX }} --version + brew install onedpl tbb + + echo "LIBRARY_PATH=$HOMEBREW_PREFIX/lib:$LIBRARY_PATH" >> $GITHUB_ENV + echo "LD_LIBRARY_PATH=$HOMEBREW_PREFIX/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV + echo "CPATH=$HOMEBREW_PREFIX/include:$CPATH" >> $GITHUB_ENV - name: Compile classic mode run: | cp -v -p artisoptions_classic.h artisoptions.h - make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec + make clean + make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} ${{ matrix.stdpar == 'ON' && ' STDPAR=ON' || ''}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec - name: Compile nebular mode run: | cp -v -p artisoptions_classic.h artisoptions.h - make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec + make clean + make MPI=${{matrix.mpi}} OPENMP=${{matrix.openmp}} ${{ matrix.stdpar == 'ON' && ' STDPAR=ON' || ''}} -j${{ steps.cpu-count.outputs.count}} sn3d exspec diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2fb353ff..179ab3e04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,8 @@ jobs: kilonova_1d_1dgrid, kilonova_1d_3dgrid, kilonova_2d_2dgrid, + kilonova_2d_2dgrid_expansionopac, + kilonova_2d_2dgrid_xcomgammaphotoion, kilonova_2d_3dgrid, nebularonezone_1d_3dgrid, nebularonezone_1d_3dgrid_limitbfest, @@ -47,26 +49,37 @@ jobs: with: fetch-depth: 0 - - name: install dependencies - if: matrix.os != 'selfhosted' + - 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-13 + if: always() + uses: fortran-lang/setup-fortran@v1 + with: + compiler: gcc + version: 13 + + - name: install gsl and openmpi run: | - git status - 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++-13" >> $GITHUB_ENV - name: CPU type and core count id: cpu-count run: | g++ -march=native -Q --help=target | grep -- '-march= ' | cut -f3 + python3 -c 'import multiprocessing; print(f"CPU count: {multiprocessing.cpu_count()}")' echo "count=$(python3 -c 'import multiprocessing; print(multiprocessing.cpu_count())')" >> $GITHUB_OUTPUT # 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_1d_3dgrid' || matrix.testname == 'classicmode_3d' - uses: actions/cache@v3 + if: ${{ startsWith(matrix.testname, 'classicmode') }} + uses: actions/cache@v4 with: path: tests/${{ matrix.testname }}_testrun/ratecoeff.dat key: tests/${{ matrix.testname }}_testrun/ratecoeff.dat-${{ github.sha }} @@ -74,16 +87,16 @@ jobs: tests/${{ matrix.testname }}_testrun/ratecoeff.dat- - name: Cache test atomic data - if: ${{ !startsWith(matrix.testname, 'classicmode_1d') }} - uses: actions/cache@v3 + if: ${{ !startsWith(matrix.testname, 'classicmode') }} + uses: actions/cache@v4 id: cache-testatomicdata with: path: tests/atomicdata_feconi.tar.xz key: tests/atomicdata_feconi.tar.xz - name: Cache test atomic data classic - if: ${{ startsWith(matrix.testname, 'classicmode_1d') }} - uses: actions/cache@v3 + if: ${{ startsWith(matrix.testname, 'classicmode') }} + uses: actions/cache@v4 id: cache-testatomicdata-classic with: path: tests/atomicdata_classic.tar.xz @@ -101,9 +114,7 @@ jobs: - name: Compile run: | - mpicxx --version - mpicxx --showme:version - make TESTMODE=${{ matrix.testmode }} MPI=ON -j${{ steps.cpu-count.outputs.count }} sn3d exspec + make TESTMODE=${{ matrix.testmode }} MPI=ON FASTMATH=OFF -j${{ steps.cpu-count.outputs.count }} sn3d exspec cp sn3d tests/${{ matrix.testname }}_testrun/ cp exspec tests/${{ matrix.testname }}_testrun/ @@ -133,7 +144,7 @@ jobs: run: cat job0/output_0-0.txt - name: Checksum job0 output files - if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' + if: always() && matrix.testmode == 'OFF' working-directory: tests/${{ matrix.testname }}_testrun run: | md5sum *.out job0/*.out | tee ../${{ matrix.testname }}_inputfiles/results_md5_job0.txt @@ -185,70 +196,80 @@ jobs: cat exspec.txt - name: Checksum job1 output files - if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' + if: always() && matrix.testmode == 'OFF' working-directory: tests/${{ matrix.testname }}_testrun run: | 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 - if: always() && matrix.os != 'selfhosted' + if: always() working-directory: tests/${{ matrix.testname }}_testrun run: | touch requirements.txt find . -name "*_res_*.out" -exec zstd -v -T0 --rm -f {} \; - name: Upload output files - uses: actions/upload-artifact@v3 - if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' + uses: actions/upload-artifact@v4 + if: always() && matrix.testmode == 'OFF' with: name: test-${{ matrix.testname }}-output path: tests/${{ matrix.testname }}_testrun - name: Upload checksum files - uses: actions/upload-artifact@v3 - if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' + uses: actions/upload-artifact@v4 + if: always() && 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 + if: always() && matrix.testmode == 'OFF' + uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: '3.12' cache: pip - name: Install artistools if: always() && matrix.testmode == 'OFF' run: | - python3 -m pip install --upgrade pip - python3 -m pip install artistools - - # upgrade all installed packages to their latest versions - # python3 -m pip list freeze --outdated | tail -n +3 | cut -w -f1 | xargs -n1 python3 -m pip install --upgrade + python3 -m pip install --upgrade pip uv + uv pip install --system artistools - name: Plot light curve if: always() && matrix.testmode == 'OFF' working-directory: tests/ run: | at plotlightcurves ${{ matrix.testname }}_testrun + at plotlightcurves --frompackets ${{ matrix.testname }}_testrun - name: Plot spectrum nebularonezone - if: always() && matrix.testname == 'nebularonezone' && matrix.testmode == 'OFF' + if: always() && matrix.testmode == 'OFF' && startsWith(matrix.testname, 'nebularonezone') + working-directory: tests/ + run: | + at plotspectra --frompackets -ts 8 ${{ matrix.testname }}_testrun + + - name: Plot spectrum classicmode_1d + if: always() && matrix.testmode == 'OFF' && startsWith(matrix.testname, 'classicmode_1d') working-directory: tests/ run: | - at plotspectra -ts 8 ${{ matrix.testname }}_testrun + at plotspectra --frompackets -t 18-22 ${{ matrix.testname }}_testrun - - name: Plot spectrum classicmode - if: always() && matrix.testname == 'classicmode' && matrix.testmode == 'OFF' + - name: Plot spectrum classicmode_3d + if: always() && matrix.testmode == 'OFF' && startsWith(matrix.testname, 'classicmode_3d') working-directory: tests/ run: | - at plotspectra -t 18-22 ${{ matrix.testname }}_testrun + at plotspectra --frompackets -t 4-6 ${{ matrix.testname }}_testrun + + - name: Plot spectrum kilonova + if: always() && matrix.testmode == 'OFF' && startsWith(matrix.testname, 'kilonova') + working-directory: tests/ + run: | + at plotspectra --frompackets -t 2 ${{ matrix.testname }}_testrun - name: Upload plot files - if: always() && matrix.os != 'selfhosted' && matrix.testmode == 'OFF' - uses: actions/upload-artifact@v3 + if: always() && matrix.testmode == 'OFF' + uses: actions/upload-artifact@v4 with: name: test-${{ matrix.testname }}-output-pdf path: tests/*.pdf @@ -279,14 +300,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Download test output - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 - name: List all files if: always() run: find . - name: Upload bundled checksum files - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: checksums path: '*_inputfiles/results_md5*.txt' diff --git a/.vscode/settings.json b/.vscode/settings.json index b8bda0698..eae03cbcb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,12 +2,11 @@ "[cpp]": { "editor.tabSize": 2, "editor.codeActionsOnSave": { - "source.fixAll": "explicit" + "source.fixAll": "always" }, "editor.formatOnSave": true }, "[yaml]": { "editor.tabSize": 4 - }, - "sonarlint.pathToCompileCommands": "${workspaceFolder}/compile_commands.json" + } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4ff501f..386de86d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,10 +15,51 @@ set(CMAKE_CXX_STANDARD_REQUIRED True) #set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) -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) +set(SN3D_SOURCES sn3d.cc +decay.cc +gammapkt.cc +grid.cc +input.cc +kpkt.cc +ltepop.cc +macroatom.cc +md5.cc +nltepop.cc +nonthermal.cc +packet.cc +radfield.cc +ratecoeff.cc +rpkt.cc +spectrum_lightcurve.cc +stats.cc +thermalbalance.cc +update_grid.cc +update_packets.cc +vpkt.cc) + add_executable(sn3d ${SN3D_SOURCES}) -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) +set(EXSPEC_SOURCES exspec.cc +decay.cc +gammapkt.cc +grid.cc +input.cc +kpkt.cc +ltepop.cc +macroatom.cc +md5.cc +nltepop.cc +nonthermal.cc +packet.cc +radfield.cc +ratecoeff.cc +rpkt.cc +spectrum_lightcurve.cc +stats.cc +thermalbalance.cc +update_grid.cc +update_packets.cc +vpkt.cc) add_executable(exspec ${EXSPEC_SOURCES}) diff --git a/Makefile b/Makefile index ce25967df..25b548e40 100644 --- a/Makefile +++ b/Makefile @@ -3,82 +3,128 @@ # place in architecture folder, e.g. build/arm64 BUILD_DIR = build/$(shell uname -m) -CXXFLAGS += -std=c++20 -fstrict-aliasing -ftree-vectorize -flto=auto -Wno-error=unknown-pragmas - ifeq ($(MPI),) - # MPI option not specified. set to true by default - MPI := ON + # MPI option not specified. set to true if mpicxx exists + ifneq (, $(shell command -v mpicxx 2> /dev/null)) + MPI := ON + else + MPI := OFF + endif endif + ifeq ($(MPI),ON) - CXX = mpicxx + CXX := mpicxx CXXFLAGS += -DMPI_ON=true BUILD_DIR := $(BUILD_DIR)_mpi +$(info $(shell mpicxx --showme:version 2> /dev/null)) else ifeq ($(MPI),OFF) else -$(error bad value for MPI option. Should be ON or OFF) + $(error bad value for MPI option. Should be ON or OFF) endif ifeq ($(TESTMODE),ON) else ifeq ($(TESTMODE),OFF) else ifeq ($(TESTMODE),) else -$(error bad value for testmode option. Should be ON or OFF) + $(error bad value for testmode option. Should be ON or OFF) endif -COMPILER_VERSION := $(shell $(CXX) --version) +COMPILER_VERSION := $(shell $(CXX) --version) +$(info $(COMPILER_VERSION)) ifneq '' '$(findstring clang,$(COMPILER_VERSION))' - COMPILER_IS_CLANG := TRUE + COMPILER_NAME := CLANG + CXXFLAGS += -flto=thin else ifneq '' '$(findstring g++,$(COMPILER_VERSION))' - COMPILER_IS_CLANG := FALSE + COMPILER_NAME := GCC + CXXFLAGS += -flto=auto +else ifneq '' '$(findstring nvc++,$(COMPILER_VERSION))' + COMPILER_NAME := NVHPC else $(warning Unknown compiler) - COMPILER_IS_CLANG := FALSE + COMPILER_NAME := unknown +endif + +$(info detected compiler is $(COMPILER_NAME)) + +CXXFLAGS += -std=c++20 -fstrict-aliasing +# CXXFLAGS += -DUSE_SIMPSON_INTEGRATOR=true + +ifneq ($(COMPILER_NAME),NVHPC) + CXXFLAGS += -ftree-vectorize -Wunknown-pragmas -Wunused-macros -Werror -MD -MP + # add -ftrivial-auto-var-init=zero when we drop gcc 11 support +endif + +# profile-guided optimisation +# generate profile: +# CXXFLAGS += -fprofile-generate="profdataraw" +# for clang, run this to convert the raw data to profdata +# llvm-profdata merge -output=profdata profdataraw/* +# compile with PGO: +# CXXFLAGS += -fprofile-use="profdataraw" + +ifeq ($(GPU),ON) + CXXFLAGS += -DGPU_ON=true -DUSE_SIMPSON_INTEGRATOR=true + BUILD_DIR := $(BUILD_DIR)_gpu +else ifeq ($(GPU),OFF) +else ifeq ($(GPU),) +else + $(error bad value for GPU option. Should be ON or OFF) endif ifeq ($(OPENMP),ON) + ifeq ($(STDPAR),ON) + $(error cannot combine OPENMP and STDPAR) + endif BUILD_DIR := $(BUILD_DIR)_openmp - ifeq ($(COMPILER_IS_CLANG),TRUE) - CXXFLAGS += -Xpreprocessor -fopenmp - LDFLAGS += -lomp - else - CXXFLAGS += -fopenmp - endif + ifeq ($(COMPILER_NAME),NVHPC) + CXXFLAGS += -mp=gpu -gpu=unified + else ifeq ($(COMPILER_NAME),CLANG) + CXXFLAGS += -Xpreprocessor -fopenmp + LDFLAGS += -lomp + else ifeq ($(COMPILER_NAME),GCC) + CXXFLAGS += -fopenmp + endif else ifeq ($(OPENMP),OFF) else ifeq ($(OPENMP),) else - $(error bad value for openmp option. Should be ON or OFF) + $(error bad value for OPENMP option. Should be ON or OFF) endif ifeq ($(STDPAR),ON) - ifeq ($(OPENMP),ON) - $(error cannot combine OPENMP and STDPAR) - endif - CXXFLAGS += -DSTDPAR_ON=true BUILD_DIR := $(BUILD_DIR)_stdpar - ifeq ($(COMPILER_IS_CLANG),TRUE) - else - # CXXFLAGS += -Xlinker -debug_snapshot - LDFLAGS += -ltbb + ifeq ($(COMPILER_NAME),NVHPC) + CXXFLAGS += -stdpar=gpu -gpu=unified + else ifeq ($(COMPILER_NAME),CLANG) + # CXXFLAGS += -fexperimental-library + LDFLAGS += -ltbb + # LDFLAGS += -Xlinker -debug_snapshot + else ifeq ($(COMPILER_NAME),GCC) + LDFLAGS += -ltbb endif + else ifeq ($(STDPAR),OFF) else ifeq ($(STDPAR),) else $(error bad value for STDPAR option. Should be ON or OFF) endif +ifneq ($(STDPAR),ON) + # triggers errors for the onedpl headers + CXXFLAGS += -Wundef +endif ifeq ($(shell uname -s),Darwin) # macOS - ifeq ($(COMPILER_IS_CLANG),FALSE) - # fixes linking on macOS with gcc - LDFLAGS += -Wl,-ld_classic - endif + ifeq ($(COMPILER_NAME),GCC) +# fixes linking on macOS with gcc + LDFLAGS += -Xlinker -ld_classic + endif ifeq ($(shell uname -m),arm64) # On Arm, -mcpu combines -march and -mtune @@ -88,29 +134,16 @@ ifeq ($(shell uname -s),Darwin) CXXFLAGS += -march=native endif - CXXFLAGS += -fno-omit-frame-pointer + CXXFLAGS += -fno-omit-frame-pointer -g # CXXFLAGS += -Rpass=loop-vectorize # CXXFLAGS += -Rpass-missed=loop-vectorize # CXXFLAGS += -Rpass-analysis=loop-vectorize # CXXFLAGS += -fopenmp-simd - # enable OpenMP for Clang - # CXXFLAGS += -Xpreprocessor -fopenmp -lomp - # add -lprofiler for gperftools # LDFLAGS += $(LIB) # LDFLAGS += -lprofiler - CXXFLAGS += $(shell pkg-config --cflags ompi) - -else ifeq ($(USER),localadmin_ccollins) - # CXX = c++ - LDFLAGS= -lgsl -lgslcblas -lm -I/home/localadmin_ccollins/gsl/include - INCLUDE = /home/localadmin_ccollins/gsl/include - LIB = /home/localadmin_ccollins/gsl/lib - CXXFLAGS += -g -I$(INCLUDE) - LDFLAGS= -L$(LIB) -lgsl -lgslcblas -lm - CXXFLAGS += -std=c++17 -Wstrict-aliasing -fstrict-aliasing #-fopenmp=libomp else # sometimes the login nodes have slighty different CPUs @@ -122,14 +155,15 @@ else # to get the current CPU architecture, run this: # g++ -march=native -Q --help=target | grep -- '-march= ' | cut -f3 - ifneq (,$(findstring juwels,$(HOSTNAME))) - CXXFLAGS += -march=skylake-avx512 - else ifneq (,$(findstring lxbk,$(HOSTNAME))) - # virgo has some AMD nodes (znver1 arch) and some Intel - CXXFLAGS += -march=cascadelake - else ifneq (,$(findstring login-q,$(HOSTNAME))) - # Cambridge icelake nodes - CXXFLAGS += -march=icelake-server + ifneq (,$(shell hostname -A | grep gsi.de)) + # virgo has some AMD nodes and some Intel. + # As of Feb 2024, the login nodes are zen3, and we select the same arch for jobs + CXXFLAGS += -march=native + else + # for GitHub actions, checksums must match with different assigned CPUs, so avoid -march=native (use lowest common denominator) + # update: all zenver3 now? + + CXXFLAGS += -march=native endif endif @@ -146,16 +180,17 @@ CXXFLAGS += $(shell pkg-config --cflags gsl) CXXFLAGS += -DHAVE_INLINE -DGSL_C99_INLINE ifeq ($(TESTMODE),ON) - CXXFLAGS += -DTESTMODE=true -DLIBCXX_ENABLE_DEBUG_MODE - # makes GitHub actions classic test run forever? - # CXXFLAGS += -D_GLIBCXX_DEBUG=1 + CXXFLAGS += -DTESTMODE=true -D_LIBCPP_DEBUG=0 + + CXXFLAGS += -D_GLIBCXX_ASSERTIONS + # CXXFLAGS += -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_BACKTRACE=1 + CXXFLAGS += -fno-omit-frame-pointer - ifeq ($(COMPILER_IS_CLANG),TRUE) - CXXFLAGS += -fsanitize=address,undefined,integer - else - CXXFLAGS += -fsanitize=address,undefined - endif + # CXXFLAGS += -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE + CXXFLAGS += -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG + + CXXFLAGS += -fsanitize=undefined,address BUILD_DIR := $(BUILD_DIR)_testmode else @@ -167,15 +202,25 @@ ifeq ($(OPTIMIZE),OFF) BUILD_DIR := $(BUILD_DIR)_optimizeoff CXXFLAGS += -O0 else - ifeq ($(FASTMATH),ON) - BUILD_DIR := $(BUILD_DIR)_fastmath - CXXFLAGS += -Ofast -ffast-math -funsafe-math-optimizations -fno-finite-math-only - else - CXXFLAGS += -O3 - endif + # ifeq ($(TESTMODE),ON) + # CXXFLAGS += -Og + # else + ifeq ($(FASTMATH),OFF) + CXXFLAGS += -O3 + BUILD_DIR := $(BUILD_DIR)_nofastmath + else + CXXFLAGS += -Ofast + + ifeq ($(COMPILER_NAME),NVHPC) + CXXFLAGS += -fast + else + CXXFLAGS += -ffast-math -funsafe-math-optimizations -fno-finite-math-only + endif + endif + # endif endif -CXXFLAGS += -Werror -Werror=undef -Winline -Wall -Wpedantic -Wredundant-decls -Wundef -Wno-unused-parameter -Wno-unused-function -Wunused-macros -Wno-inline -Wsign-compare +CXXFLAGS += -Winline -Wall -Wpedantic -Wredundant-decls -Wno-unused-parameter -Wno-unused-function -Wno-inline -Wsign-compare ### use pg when you want to use gprof profiler @@ -196,7 +241,7 @@ all: sn3d exspec $(BUILD_DIR)/%.o: %.cc artisoptions.h Makefile @mkdir -p $(@D) - $(CXX) $(CXXFLAGS) -MD -MP -c $< -o $@ + $(CXX) $(CXXFLAGS) -c $< -o $@ $(BUILD_DIR)/sn3d.o $(BUILD_DIR)/exspec.o: version.h artisoptions.h Makefile @@ -218,7 +263,6 @@ exspec: $(exspec_objects) artisoptions.h Makefile version.h: @echo "constexpr const char* GIT_VERSION = \"$(shell git describe --dirty --always --tags)\";" > version.h - @echo "constexpr const char* GIT_HASH = \"$(shell git rev-parse HEAD)\";" >> version.h # requires git > 2.22 # @echo "constexpr const char* GIT_BRANCH = \"$(shell git branch --show)\";" >> version.h @echo "constexpr const char* GIT_BRANCH = \"$(shell git symbolic-ref --short HEAD 2>/dev/null || git rev-parse --short HEAD )\";" >> version.h diff --git a/README.old b/README.old index bcfeb17ae..4248742bf 100644 --- a/README.old +++ b/README.old @@ -128,7 +128,7 @@ code until 2012-07-30: for (level == 0) and (ion == 0) to (level == 0) and (get_ionstage(element,ion) == 0). This allows us to use model atoms which have an ionized species as the lower boundary. - - [FIXED] rpkt_emiss and compton_emiss were not properly + - [FIXED] dep_estimator_gamma and compton_emiss were not properly renormalised in non-MPI runs 2010-04-01, v111_omp diff --git a/artisoptions_christinenonthermal.h b/artisoptions_christinenonthermal.h index 53d0b6f92..e0df81afe 100644 --- a/artisoptions_christinenonthermal.h +++ b/artisoptions_christinenonthermal.h @@ -1,3 +1,4 @@ +#pragma once #ifndef ARTISOPTIONS_H // NOLINT(llvm-header-guard) #define ARTISOPTIONS_H // NOLINTBEGIN(modernize*,misc-unused-parameters) @@ -53,9 +54,9 @@ constexpr bool DIPOLE = false; constexpr bool POL_ON = false; constexpr bool VPKT_ON = false; +constexpr bool VPKT_WRITE_CONTRIBS = false; constexpr bool TRACK_ION_STATS = false; -constexpr bool TRACK_ION_MASTATS = false; constexpr double MINPOP = 1e-40; @@ -144,5 +145,11 @@ constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; +constexpr bool EXPANSIONOPACITIES_ON = false; + +constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = false; + +constexpr bool USE_XCOM_GAMMAPHOTOION = false; + // NOLINTEND(modernize*,misc-unused-parameters) #endif // ARTISOPTIONS_H \ No newline at end of file diff --git a/artisoptions_classic.h b/artisoptions_classic.h index 4e395310f..caae7b1df 100644 --- a/artisoptions_classic.h +++ b/artisoptions_classic.h @@ -1,3 +1,4 @@ +#pragma once #ifndef ARTISOPTIONS_H // NOLINT(llvm-header-guard) #define ARTISOPTIONS_H // NOLINTBEGIN(modernize*,misc-unused-parameters) @@ -46,9 +47,9 @@ constexpr bool DIPOLE = true; constexpr bool POL_ON = true; constexpr bool VPKT_ON = false; +constexpr bool VPKT_WRITE_CONTRIBS = false; constexpr bool TRACK_ION_STATS = false; -constexpr bool TRACK_ION_MASTATS = false; constexpr double MINPOP = 1e-30; @@ -140,5 +141,11 @@ constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; +constexpr bool EXPANSIONOPACITIES_ON = false; + +constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = false; + +constexpr bool USE_XCOM_GAMMAPHOTOION = false; + // NOLINTEND(modernize*,misc-unused-parameters) #endif // ARTISOPTIONS_H \ No newline at end of file diff --git a/artisoptions_doc.md b/artisoptions_doc.md index f53ffff75..150dd1258 100644 --- a/artisoptions_doc.md +++ b/artisoptions_doc.md @@ -71,7 +71,6 @@ constexpr bool POL_ON; constexpr bool VPKT_ON; constexpr bool TRACK_ION_STATS; -constexpr bool TRACK_ION_MASTATS; constexpr double MINPOP; @@ -134,7 +133,7 @@ constexpr double SF_EMAX; constexpr double SF_EMIN; // trigger a Spencer-Fano solution at least once every n timesteps -// 0 can only use solutions from previous NLTE iterations on the current timestep +// 0 can only re-use solutions from previous NLTE iterations of the current timestep // <=-1 will always solve the SF equation for every iteration of every timestep constexpr int SF_MAX_TIMESTEPS_BETWEEN_SOLUTIONS; @@ -220,5 +219,7 @@ constexpr bool KEEP_ALL_RESTART_FILES; // multiply bound-free cooling coefficient by upper level population instead of the upper ion target level population constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP; +// Use XCOM data for gamma photoionisation instead of Si+Fe Equation 2 of Ambwani & Sutherland (1988), Veigele (1973) +constexpr bool USE_XCOM_GAMMAPHOTOION; ``` \ No newline at end of file diff --git a/artisoptions_kilonova_lte.h b/artisoptions_kilonova_lte.h index 250824bad..a5692c0de 100644 --- a/artisoptions_kilonova_lte.h +++ b/artisoptions_kilonova_lte.h @@ -1,3 +1,4 @@ +#pragma once #ifndef ARTISOPTIONS_H // NOLINT(llvm-header-guard) #define ARTISOPTIONS_H // NOLINTBEGIN(modernize*,misc-unused-parameters) @@ -20,7 +21,7 @@ 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 FORCE_SAHA_ION_BALANCE(int element_z) { return true; } constexpr bool single_level_top_ion = false; @@ -48,9 +49,9 @@ constexpr bool DIPOLE = false; constexpr bool POL_ON = false; constexpr bool VPKT_ON = false; +constexpr bool VPKT_WRITE_CONTRIBS = false; constexpr bool TRACK_ION_STATS = false; -constexpr bool TRACK_ION_MASTATS = false; constexpr double MINPOP = 1e-40; @@ -139,5 +140,11 @@ constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; +constexpr bool EXPANSIONOPACITIES_ON = false; + +constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = false; + +constexpr bool USE_XCOM_GAMMAPHOTOION = false; + // NOLINTEND(modernize*,misc-unused-parameters) #endif // ARTISOPTIONS_H diff --git a/artisoptions_nltenebular.h b/artisoptions_nltenebular.h index ca2704692..178242722 100644 --- a/artisoptions_nltenebular.h +++ b/artisoptions_nltenebular.h @@ -1,3 +1,4 @@ +#pragma once #ifndef ARTISOPTIONS_H // NOLINT(llvm-header-guard) #define ARTISOPTIONS_H // NOLINTBEGIN(modernize*,misc-unused-parameters) @@ -53,9 +54,9 @@ constexpr bool DIPOLE = false; constexpr bool POL_ON = false; constexpr bool VPKT_ON = false; +constexpr bool VPKT_WRITE_CONTRIBS = false; constexpr bool TRACK_ION_STATS = false; -constexpr bool TRACK_ION_MASTATS = false; constexpr double MINPOP = 1e-40; @@ -151,5 +152,11 @@ constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = false; +constexpr bool EXPANSIONOPACITIES_ON = false; + +constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = false; + +constexpr bool USE_XCOM_GAMMAPHOTOION = false; + // NOLINTEND(modernize*,misc-unused-parameters) #endif // ARTISOPTIONS_H diff --git a/artisoptions_nltewithoutnonthermal.h b/artisoptions_nltewithoutnonthermal.h index f295413f4..4abf2fec6 100644 --- a/artisoptions_nltewithoutnonthermal.h +++ b/artisoptions_nltewithoutnonthermal.h @@ -1,3 +1,4 @@ +#pragma once #ifndef ARTISOPTIONS_H // NOLINT(llvm-header-guard) #define ARTISOPTIONS_H // NOLINTBEGIN(modernize*,misc-unused-parameters) @@ -51,9 +52,9 @@ constexpr bool DIPOLE = true; constexpr bool POL_ON = true; constexpr bool VPKT_ON = false; +constexpr bool VPKT_WRITE_CONTRIBS = false; constexpr bool TRACK_ION_STATS = false; -constexpr bool TRACK_ION_MASTATS = false; constexpr double MINPOP = 1e-40; @@ -143,5 +144,11 @@ constexpr bool KEEP_ALL_RESTART_FILES = false; constexpr bool BFCOOLING_USELEVELPOPNOTIONPOP = true; +constexpr bool EXPANSIONOPACITIES_ON = false; + +constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = false; + +constexpr bool USE_XCOM_GAMMAPHOTOION = false; + // NOLINTEND(modernize*,misc-unused-parameters) #endif // ARTISOPTIONS_H \ No newline at end of file diff --git a/atomic.cc b/atomic.cc deleted file mode 100644 index a6f01bebb..000000000 --- a/atomic.cc +++ /dev/null @@ -1,499 +0,0 @@ -#include "atomic.h" - -#include -#include - -#include "artisoptions.h" -#include "grid.h" -#include "ltepop.h" -#include "sn3d.h" - -// last photoion cross section point as a factor of nu_edge = last_phixs_nuovernuedge -double last_phixs_nuovernuedge = -1; - -// highest number of ions for any element -int maxnions = 0; - -// number of ions of any element -int includedions = 0; - -// number of ions of any element excluding the highest ionisation stage of each element -int includedions_excludehighest = 0; -std::array phixs_file_version_exists; - -auto get_continuumindex_phixstargetindex(const int element, const int ion, const int level, - const int phixstargetindex) -> int -/// Returns the index of the continuum associated to the given level. -{ - return globals::elements[element].ions[ion].levels[level].cont_index - phixstargetindex; -} - -auto get_phixtargetindex(const int element, const int ion, const int level, const int upperionlevel) -> int { - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { - if (upperionlevel == get_phixsupperlevel(element, ion, level, phixstargetindex)) { - return phixstargetindex; - } - } - printout("Could not find phixstargetindex\n"); - abort(); - return -1; -} - -auto get_continuumindex(const int element, const int ion, const int level, const int upperionlevel) -> int -/// Returns the index of the continuum associated to the given level. -{ - const int phixstargetindex = get_phixtargetindex(element, ion, level, upperionlevel); - return get_continuumindex_phixstargetindex(element, ion, level, phixstargetindex); -} - -auto get_tau_sobolev(const int modelgridindex, const int lineindex, const double t_current) -> double { - const int element = globals::linelist[lineindex].elementindex; - const int ion = globals::linelist[lineindex].ionindex; - const int lower = globals::linelist[lineindex].lowerlevelindex; - const int upper = globals::linelist[lineindex].upperlevelindex; - - const double n_l = get_levelpop(modelgridindex, element, ion, lower); - const double n_u = get_levelpop(modelgridindex, element, ion, upper); - - const double nu_trans = (epsilon(element, ion, upper) - epsilon(element, ion, lower)) / H; - const double A_ul = einstein_spontaneous_emission(lineindex); - const double B_ul = CLIGHTSQUAREDOVERTWOH / pow(nu_trans, 3) * A_ul; - const double B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; - - const double tau_sobolev = std::max(0., (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_current); - return tau_sobolev; -} - -auto get_nnion_tot(int modelgridindex) -> double -// total density of nuclei -{ - double nntot = 0.; - for (int element = 0; element < get_nelements(); element++) { - nntot += grid::get_elem_numberdens(modelgridindex, element); - } - - return nntot; -} - -auto is_nlte(const int element, const int ion, const int level) -> bool -// Returns true if (element,ion,level) is to be treated in nlte. -// (note this function returns true for the ground state, -// although it is stored separately from the excited NLTE states) -{ - return LEVEL_IS_NLTE(get_atomicnumber(element), get_ionstage(element, ion), - level); // defined in artisoptions.h -} - -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 (!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) -> double -/// Calculates the photoionisation cross-section at frequency nu out of the atomic data. -/// Input: - edge frequency nu_edge of the desired bf-continuum -/// - nu -{ - // if (nu < nu_edge || nu > nu_edge * 1.05) - // return 0; - // else - // return 1.; - // return 1. * pow(nu_edge / nu, 3); - - float sigma_bf = 0.; - - if (phixs_file_version_exists[1] && !phixs_file_version_exists[2]) { - // classic mode: no interpolation - if (nu == nu_edge) { - sigma_bf = photoion_xs[0]; - } else if (nu <= nu_edge * (1 + globals::NPHIXSNUINCREMENT * globals::NPHIXSPOINTS)) { - const int i = static_cast(floor((nu - nu_edge) / (globals::NPHIXSNUINCREMENT * nu_edge))); - sigma_bf = photoion_xs[i]; - } else { - /// use a parameterization of sigma_bf by the Kramers formula - /// which anchor point should we take ??? the cross-section at the edge or at the highest grid point ??? - /// so far the highest grid point, otherwise the cross-section is not continuous - sigma_bf = photoion_xs[globals::NPHIXSPOINTS - 1] * - pow(nu_edge * (1 + globals::NPHIXSNUINCREMENT * globals::NPHIXSPOINTS) / nu, 3); - } - return sigma_bf; - } - - const double ireal = (nu / nu_edge - 1.0) / globals::NPHIXSNUINCREMENT; - const int i = floor(ireal); - - if (i < 0) { - sigma_bf = 0.0; - // printout("[warning] photoionization_crosssection was called with nu=%g < nu_edge=%g\n",nu,nu_edge); - // printout("[warning] element %d, ion %d, level %d, epsilon %g, ionpot - // %g\n",element,ion,level,epsilon(element,ion,level),elements[element].ions[ion].ionpot); printout("[warning] - // element %d, ion+1 %d, level %d epsilon %g, ionpot - // %g\n",element,ion+1,0,epsilon(element,ion+1,0),elements[element].ions[ion].ionpot); printout("[warning] - // photoionization_crosssection %g\n",sigma_bf); abort(); - } else if (i < globals::NPHIXSPOINTS - 1) { - // sigma_bf = globals::elements[element].ions[ion].levels[level].photoion_xs[i]; - - const double sigma_bf_a = photoion_xs[i]; - const double sigma_bf_b = photoion_xs[i + 1]; - const double factor_b = ireal - i; - sigma_bf = ((1. - factor_b) * sigma_bf_a) + (factor_b * sigma_bf_b); - } else { - /// use a parameterization of sigma_bf by the Kramers formula - /// which anchor point should we take ??? the cross-section at the edge or at the highest grid point ??? - /// so far the highest grid point, otherwise the cross-section is not continuous - const double nu_max_phixs = nu_edge * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table - sigma_bf = photoion_xs[globals::NPHIXSPOINTS - 1] * pow(nu_max_phixs / nu, 3); - } - - // if (sigma_bf < 0) - // { - // printout("[warning] photoionization_crosssection returns negative cross-section %g\n",sigma_bf); - // printout("[warning] nu=%g, nu_edge=%g\n",nu,nu_edge); - // printout("[warning] xs@edge=%g, - // xs@maxfreq\n",elements[element].ions[ion].levels[level].photoion_xs[0],elements[element].ions[ion].levels[level].photoion_xs[NPHIXSPOINTS-1]); - // printout("[warning] element %d, ion %d, level %d, epsilon %g, ionpot - // %g\n",element,ion,level,epsilon(element,ion,level),elements[element].ions[ion].ionpot); - // } - - return sigma_bf; -} - -void set_nelements(const int nelements_in) { globals::elements.resize(nelements_in); } - -auto get_nelements() -> int { return static_cast(globals::elements.size()); } - -auto get_atomicnumber(const int element) -> int -/// Returns the atomic number associated with a given elementindex. -{ - assert_testmodeonly(element >= 0); - assert_testmodeonly(element < get_nelements()); - return globals::elements[element].anumber; -} - -auto get_elementindex(const int Z) -> int -/// Returns the elementindex associated with a given atomic number. -/// If there is no element with the given atomic number in the atomic data -/// a negative value is returned to flag this event. -{ - 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();; - return -100; -} - -void update_includedions_maxnions() { - includedions = 0; - includedions_excludehighest = 0; - maxnions = 0; - for (int element = 0; element < get_nelements(); element++) { - includedions += get_nions(element); - includedions_excludehighest += get_nions(element) > 0 ? get_nions(element) - 1 : 0; - maxnions = std::max(maxnions, get_nions(element)); - } -} - -auto get_includedions() -> int -// returns the number of ions of all elements combined -{ - return includedions; -} - -auto get_includedions_excludehighest() -> int -// returns the number of ions of all elements combined -{ - return includedions_excludehighest; -} - -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 -{} - -auto get_max_nions() -> int { - // number greater than or equal to nions(element) for all elements - return maxnions; -} - -auto get_nions(const int element) -> int -/// Returns the number of ions associated with a specific element given by -/// its elementindex. -{ - assert_testmodeonly(element < get_nelements()); - return globals::elements[element].nions; -} - -auto get_ionstage(const int element, const int ion) -> int -/// Returns the ionisation stage of an ion specified by its elementindex and -/// ionindex. -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return globals::elements[element].ions[ion].ionstage; -} - -auto get_nlevels(const int element, const int ion) -> int -/// Returns the number of levels associated with with a specific ion given -/// its elementindex and ionindex. -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - 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 -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return globals::elements[element].ions[ion].nlevels_nlte; -} - -auto get_nlevels_groundterm(const int element, const int ion) -> int { - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return globals::elements[element].ions[ion].nlevels_groundterm; -} - -auto get_ionisinglevels(const int element, const int ion) -> int -/// Returns the number of levels associated with an ion that -/// have energies below the ionisation threshold. -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return globals::elements[element].ions[ion].ionisinglevels; -} - -auto get_uniqueionindex(const int element, const int ion) -> int -// Get an index for an ionstage of an element that is unique for every ion of every element -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - - return globals::elements[element].uniqueionindexstart + ion; -} - -auto get_ionfromuniqueionindex(const int allionsindex) -> std::tuple { - assert_testmodeonly(allionsindex < get_includedions()); - - for (int element = 0; element < get_nelements(); element++) { - if (get_nions(element) == 0) { - continue; - } - const int ion = allionsindex - globals::elements[element].uniqueionindexstart; - if (ion < get_nions(element)) { - assert_testmodeonly(get_uniqueionindex(element, ion) == allionsindex); - return {element, ion}; - } - } - assert_always(false); // allionsindex too high to be valid -} - -auto get_uniquelevelindex(const int element, const int ion, const int level) -> int -// Get an index for level of an ionstage of an element that is unique across every ion of every element -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - - int index = 0; - for (int e = 0; e < element; e++) { - const int nions = get_nions(e); - for (int i = 0; i < nions; i++) { - index += get_nlevels(e, i); - } - } - // selected element, levels from lower ions - for (int i = 0; i < ion; i++) { - index += get_nlevels(element, i); - } - // lower levels in selected element/ion - index += level; - - assert_testmodeonly(index == globals::elements[element].ions[ion].levels[level].uniquelevelindex); - return index; -} - -void get_levelfromuniquelevelindex(const int alllevelsindex, int *element, int *ion, int *level) -// inverse of get_uniquelevelindex(). get the element/ion/level from a unique level index -{ - int allionsindex_thisionfirstlevel = 0; - for (int e = 0; e < get_nelements(); e++) { - const int nions = get_nions(e); - for (int i = 0; i < nions; i++) { - if ((alllevelsindex - allionsindex_thisionfirstlevel) >= get_nlevels(e, i)) { - allionsindex_thisionfirstlevel += get_nlevels(e, i); // skip this ion - } else { - *element = e; - *ion = i; - *level = alllevelsindex - allionsindex_thisionfirstlevel; - assert_testmodeonly(get_uniquelevelindex(*element, *ion, *level) == alllevelsindex); - return; - } - } - } - assert_always(false); // alllevelsindex too high to be valid -} - -auto epsilon(const int element, const int ion, const int level) -> double -/// Returns the energy of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - return globals::elements[element].ions[ion].levels[level].epsilon; -} - -auto stat_weight(const int element, const int ion, const int level) -> double -/// Returns the statistical weight of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - return globals::elements[element].ions[ion].levels[level].stat_weight; -} - -auto get_maxrecombininglevel(const int element, const int ion) -> int -/// Returns the number of bf-continua associated with ion ion of element element. -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return globals::elements[element].ions[ion].maxrecombininglevel; -} - -auto ion_has_superlevel(const int element, const int ion) -> bool { - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - return (get_nlevels(element, ion) > get_nlevels_nlte(element, ion) + 1); -} - -auto get_ndowntrans(const int element, const int ion, const int level) -> int -// the number of downward bound-bound transitions from the specified level -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - return globals::elements[element].ions[ion].levels[level].ndowntrans; -} - -auto get_nuptrans(const int element, const int ion, const int level) -> int -// the number of upward bound-bound transitions from the specified level -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - return globals::elements[element].ions[ion].levels[level].nuptrans; -} - -void set_ndowntrans(const int element, const int ion, const int level, const int ndowntrans) -// the number of downward bound-bound transitions from the specified level -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - globals::elements[element].ions[ion].levels[level].ndowntrans = ndowntrans; -} - -void set_nuptrans(const int element, const int ion, const int level, const int nuptrans) -// the number of upward bound-bound transitions from the specified level -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - globals::elements[element].ions[ion].levels[level].nuptrans = nuptrans; -} - -auto get_nphixstargets(const int element, const int ion, const int level) -> int -/// Returns the number of target states for photoionization of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - const int nions = get_nions(element); - const int nionisinglevels = get_ionisinglevels(element, ion); - if ((ion < nions - 1) && (level < nionisinglevels)) { - return globals::elements[element].ions[ion].levels[level].nphixstargets; - } - return 0; -} - -auto get_phixsupperlevel(const int element, const int ion, const int level, const int phixstargetindex) -> int -/// Returns the level index of a target state for photoionization of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - assert_testmodeonly(phixstargetindex >= 0); - assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); - - return globals::elements[element].ions[ion].levels[level].phixstargets[phixstargetindex].levelindex; -} - -auto get_phixs_threshold(const int element, const int ion, const int level, const int phixstargetindex) -> double -/// Returns the energy of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); - // const double phixs_threshold_stored = globals::elements[element].ions[ion].levels[level].phixs_threshold; - // if (phixs_threshold_stored > 0.) - // return phixs_threshold_stored; - // else - { - const int upperlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); - const double E_threshold = epsilon(element, ion + 1, upperlevel) - epsilon(element, ion, level); - return E_threshold; - } -} - -auto get_phixsprobability(const int element, const int ion, const int level, const int phixstargetindex) -> double -/// Returns the probability of a target state for photoionization of (element,ion,level). -{ - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - assert_testmodeonly(phixstargetindex >= 0); - assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); - - return globals::elements[element].ions[ion].levels[level].phixstargets[phixstargetindex].probability; -} - -auto einstein_spontaneous_emission(const int lineindex) -> double -// double einstein_spontaneous_emission(int element, int ion, int upper, int lower) -/// reads A_ul from levellist which consists of -/// (epsilon_upper; 0) | (g_upper; 0) | (A_upper,upper-1; f_upper,upper-1) | (A_uppper,upper-2; f_upper,upper-2) | ... | -/// (A_upper,1; f_upper,1) -{ - return globals::linelist[lineindex].einstein_A; -} - -auto photoionization_crosssection(const int element, const int ion, const int level, const double nu_edge, - const double nu) -> double { - assert_testmodeonly(element < get_nelements()); - assert_testmodeonly(ion < get_nions(element)); - assert_testmodeonly(level < get_nlevels(element, ion)); - return photoionization_crosssection_fromtable(globals::elements[element].ions[ion].levels[level].photoion_xs, nu_edge, - nu); -} diff --git a/atomic.h b/atomic.h index a1c36a9a6..6461e3b4b 100644 --- a/atomic.h +++ b/atomic.h @@ -1,54 +1,487 @@ +#pragma once #ifndef ATOMIC_H #define ATOMIC_H #include +#include -extern double +#include "grid.h" +#include "ltepop.h" +#include "sn3d.h" + +// highest number of ions for any element +inline int maxnions = 0; + +// number of ions of any element +inline int includedions = 0; + +// total number of levels of any element +inline int includedlevels = 0; + +inline double last_phixs_nuovernuedge; // last photoion cross section point as a factor of nu_edge = last_phixs_nuovernuedge -constexpr std::array phixsdata_filenames = {"version0ignore", "phixsdata.txt", "phixsdata_v2.txt"}; -extern std::array phixs_file_version_exists; // first value in this array is not used but exists so the +inline std::array phixs_file_version_exists; // first value in this array is not used but exists so the // indexes match those of the phixsdata_filenames array +constexpr std::array phixsdata_filenames = {"version0ignore", "phixsdata.txt", "phixsdata_v2.txt"}; + +[[nodiscard]] inline auto get_nelements() -> int { return static_cast(globals::elements.size()); } + +inline auto get_nnion_tot(int modelgridindex) -> double +// total density of nuclei +{ + double nntot = 0.; + for (int element = 0; element < get_nelements(); element++) { + nntot += grid::get_elem_numberdens(modelgridindex, element); + } + + return nntot; +} + +inline auto get_nions(const int element) -> int +/// Returns the number of ions associated with a specific element given by +/// its elementindex. +{ + assert_testmodeonly(element < get_nelements()); + return globals::elements[element].nions; +} + +inline auto get_nlevels(const int element, const int ion) -> int +/// Returns the number of levels associated with with a specific ion given +/// its elementindex and ionindex. +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].nlevels; +} + +[[nodiscard]] inline auto epsilon(const int element, const int ion, const int level) -> double +/// Returns the energy of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + return globals::elements[element].ions[ion].levels[level].epsilon; +} + +[[nodiscard]] inline auto get_ionstage(const int element, const int ion) -> int +/// Returns the ionisation stage of an ion specified by its elementindex and +/// ionindex. +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].ionstage; +} + +[[nodiscard]] inline auto get_ionisinglevels(const int element, const int ion) -> int +/// Returns the number of levels associated with an ion that +/// have energies below the ionisation threshold. +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].ionisinglevels; +} + +inline auto get_nphixstargets(const int element, const int ion, const int level) -> int +/// Returns the number of target states for photoionization of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(level < get_nlevels(element, ion)); + const int nions = get_nions(element); + assert_testmodeonly(ion < nions); + if ((ion < nions - 1) && (level < get_ionisinglevels(element, ion))) { + return globals::elements[element].ions[ion].levels[level].nphixstargets; + } + return 0; +} + +[[nodiscard]] inline auto get_phixsupperlevel(const int element, const int ion, const int level, + const int phixstargetindex) -> int +/// Returns the level index of a target state for photoionization of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + assert_testmodeonly(phixstargetindex >= 0); + assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); + + return globals::elements[element].ions[ion].levels[level].phixstargets[phixstargetindex].levelindex; +} + +[[nodiscard]] inline auto get_phixsprobability(const int element, const int ion, const int level, + const int phixstargetindex) -> double +/// Returns the probability of a target state for photoionization of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + assert_testmodeonly(phixstargetindex >= 0); + assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); + + return globals::elements[element].ions[ion].levels[level].phixstargets[phixstargetindex].probability; +} + +[[nodiscard]] inline auto einstein_spontaneous_emission(const int lineindex) -> double +// double einstein_spontaneous_emission(int element, int ion, int upper, int lower) +/// reads A_ul from levellist which consists of +/// (epsilon_upper; 0) | (g_upper; 0) | (A_upper,upper-1; f_upper,upper-1) | (A_uppper,upper-2; f_upper,upper-2) | ... | +/// (A_upper,1; f_upper,1) +{ + return globals::linelist[lineindex].einstein_A; +} + +[[nodiscard]] inline auto stat_weight(const int element, const int ion, const int level) -> double +/// Returns the statistical weight of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + return globals::elements[element].ions[ion].levels[level].stat_weight; +} + +[[nodiscard]] inline auto get_maxrecombininglevel(const int element, const int ion) -> int +/// Returns the number of bf-continua associated with ion ion of element element. +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].maxrecombininglevel; +} + +[[nodiscard]] inline auto photoionization_crosssection_fromtable(const float *const photoion_xs, const double nu_edge, + const double nu) -> double +/// Calculates the photoionisation cross-section at frequency nu out of the atomic data. +/// Input: - edge frequency nu_edge of the desired bf-continuum +/// - nu +{ + // if (nu < nu_edge || nu > nu_edge * 1.05) + // return 0; + // else + // return 1.; + // return 1. * pow(nu_edge / nu, 3); + + float sigma_bf = 0.; + + if (phixs_file_version_exists[1] && !phixs_file_version_exists[2]) { + // classic mode: no interpolation + if (nu == nu_edge) { + sigma_bf = photoion_xs[0]; + } else if (nu <= nu_edge * (1 + globals::NPHIXSNUINCREMENT * globals::NPHIXSPOINTS)) { + const int i = static_cast(floor((nu - nu_edge) / (globals::NPHIXSNUINCREMENT * nu_edge))); + sigma_bf = photoion_xs[i]; + } else { + /// use a parameterization of sigma_bf by the Kramers formula + /// which anchor point should we take ??? the cross-section at the edge or at the highest grid point ??? + /// so far the highest grid point, otherwise the cross-section is not continuous + sigma_bf = photoion_xs[globals::NPHIXSPOINTS - 1] * + pow(nu_edge * (1 + globals::NPHIXSNUINCREMENT * globals::NPHIXSPOINTS) / nu, 3); + } + return sigma_bf; + } + + const double ireal = (nu / nu_edge - 1.0) / globals::NPHIXSNUINCREMENT; + const int i = floor(ireal); + + if (i < 0) { + sigma_bf = 0.; + // printout("[warning] photoionization_crosssection was called with nu=%g < nu_edge=%g\n",nu,nu_edge); + // printout("[warning] element %d, ion %d, level %d, epsilon %g, ionpot + // %g\n",element,ion,level,epsilon(element,ion,level),elements[element].ions[ion].ionpot); printout("[warning] + // element %d, ion+1 %d, level %d epsilon %g, ionpot + // %g\n",element,ion+1,0,epsilon(element,ion+1,0),elements[element].ions[ion].ionpot); printout("[warning] + // photoionization_crosssection %g\n",sigma_bf); abort(); + } else if (i < globals::NPHIXSPOINTS - 1) { + // sigma_bf = globals::elements[element].ions[ion].levels[level].photoion_xs[i]; + + const double sigma_bf_a = photoion_xs[i]; + const double sigma_bf_b = photoion_xs[i + 1]; + const double factor_b = ireal - i; + sigma_bf = ((1. - factor_b) * sigma_bf_a) + (factor_b * sigma_bf_b); + } else { + /// use a parameterization of sigma_bf by the Kramers formula + /// which anchor point should we take ??? the cross-section at the edge or at the highest grid point ??? + /// so far the highest grid point, otherwise the cross-section is not continuous + const double nu_max_phixs = nu_edge * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table + sigma_bf = photoion_xs[globals::NPHIXSPOINTS - 1] * pow(nu_max_phixs / nu, 3); + } + + // if (sigma_bf < 0) + // { + // printout("[warning] photoionization_crosssection returns negative cross-section %g\n",sigma_bf); + // printout("[warning] nu=%g, nu_edge=%g\n",nu,nu_edge); + // printout("[warning] xs@edge=%g, + // xs@maxfreq\n",elements[element].ions[ion].levels[level].photoion_xs[0],elements[element].ions[ion].levels[level].photoion_xs[NPHIXSPOINTS-1]); + // printout("[warning] element %d, ion %d, level %d, epsilon %g, ionpot + // %g\n",element,ion,level,epsilon(element,ion,level),elements[element].ions[ion].ionpot); + // } + + return sigma_bf; +} + +[[nodiscard]] inline auto photoionization_crosssection(const int element, const int ion, const int level, + const double nu_edge, const double nu) -> double { + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + return photoionization_crosssection_fromtable(globals::elements[element].ions[ion].levels[level].photoion_xs, nu_edge, + nu); +} + +[[nodiscard]] inline auto get_tau_sobolev(const int modelgridindex, const int lineindex, const double t_current, + bool sub_updown) -> double { + const int element = globals::linelist[lineindex].elementindex; + const int ion = globals::linelist[lineindex].ionindex; + const int lower = globals::linelist[lineindex].lowerlevelindex; + const int upper = globals::linelist[lineindex].upperlevelindex; + + const double n_l = get_levelpop(modelgridindex, element, ion, lower); + + const double nu_trans = (epsilon(element, ion, upper) - epsilon(element, ion, lower)) / H; + const double A_ul = einstein_spontaneous_emission(lineindex); + const double B_ul = CLIGHTSQUAREDOVERTWOH / pow(nu_trans, 3) * A_ul; + const double B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; + + if (sub_updown) { + const double n_u = get_levelpop(modelgridindex, element, ion, upper); + return (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_current; + } + return B_lu * n_l * HCLIGHTOVERFOURPI * t_current; +} + +inline void set_nelements(const int nelements_in) { globals::elements.resize(nelements_in); } + +inline auto get_atomicnumber(const int element) -> int +/// Returns the atomic number associated with a given elementindex. +{ + assert_testmodeonly(element >= 0); + assert_testmodeonly(element < get_nelements()); + return globals::elements[element].anumber; +} + +[[nodiscard]] inline auto is_nlte(const int element, const int ion, const int level) -> bool +// Returns true if (element,ion,level) is to be treated in nlte. +// (note this function returns true for the ground state, +// although it is stored separately from the excited NLTE states) +{ + return LEVEL_IS_NLTE(get_atomicnumber(element), get_ionstage(element, ion), + level); // defined in artisoptions.h +} + +inline auto get_elementindex(const int Z) -> int +/// Returns the elementindex associated with a given atomic number. +/// If there is no element with the given atomic number in the atomic data +/// a negative value is returned to flag this event. +{ + const auto elem = + std::ranges::find_if(globals::elements, [Z](const Element &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();; + return -100; +} + +inline void update_includedionslevels_maxnions() { + includedions = 0; + includedlevels = 0; + maxnions = 0; + for (int element = 0; element < get_nelements(); element++) { + includedions += get_nions(element); + maxnions = std::max(maxnions, get_nions(element)); + for (int ion = 0; ion < get_nions(element); ion++) { + includedlevels += get_nlevels(element, ion); + } + } +} + +inline auto get_includedions() -> int +// returns the number of ions of all elements combined +{ + return includedions; +} + +inline auto get_includedlevels() -> int +// returns the number of ions of all elements combined +{ + return includedlevels; +} + +[[nodiscard]] inline auto get_max_nions() -> int { + // number greater than or equal to nions(element) for all elements + return maxnions; +} + +[[nodiscard]] inline auto elem_has_nlte_levels(const int element) -> bool { + return globals::elements[element].has_nlte_levels; +} + +[[nodiscard]] inline 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; +} + +[[nodiscard]] inline 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 +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].nlevels_nlte; +} + +[[nodiscard]] inline 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 (!is_nlte(element, ion, level) && level != 0 && (get_nlevels_nlte(element, ion) > 0)); +} + +[[nodiscard]] inline auto get_nlevels_groundterm(const int element, const int ion) -> int { + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return globals::elements[element].ions[ion].nlevels_groundterm; +} + +[[nodiscard]] inline auto get_uniqueionindex(const int element, const int ion) -> int +// Get an index for an ionstage of an element that is unique for every ion of every element +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + + const auto uniqueionindex = globals::elements[element].uniqueionindexstart + ion; + assert_testmodeonly(uniqueionindex < get_includedions()); + + return uniqueionindex; +} + +[[nodiscard]] inline auto get_ionfromuniqueionindex(const int allionsindex) -> std::tuple { + assert_testmodeonly(allionsindex < get_includedions()); + + for (int element = 0; element < get_nelements(); element++) { + if (get_nions(element) == 0) { + continue; + } + const int ion = allionsindex - globals::elements[element].uniqueionindexstart; + if (ion < get_nions(element)) { + assert_testmodeonly(get_uniqueionindex(element, ion) == allionsindex); + return {element, ion}; + } + } + assert_always(false); // allionsindex too high to be valid +} + +[[nodiscard]] inline auto get_uniquelevelindex(const int element, const int ion, const int level) -> int +// Get an index for level of an ionstage of an element that is unique across every ion of every element +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + const auto uniquelevelindex = globals::elements[element].ions[ion].uniquelevelindexstart + level; + assert_testmodeonly(uniquelevelindex < get_includedlevels()); + + return uniquelevelindex; +} + +[[nodiscard]] inline auto get_levelfromuniquelevelindex(const int alllevelsindex) -> std::tuple +// inverse of get_uniquelevelindex(). get the element/ion/level from a unique level index +{ + for (int element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + if (get_nlevels(element, ion) == 0) { + continue; + } + const int level = alllevelsindex - globals::elements[element].ions[ion].uniquelevelindexstart; + if (level < get_nlevels(element, ion)) { + assert_testmodeonly(get_uniquelevelindex(element, ion, level) == alllevelsindex); + return {element, ion, level}; + } + } + } + assert_always(false); // alllevelsindex too high to be valid +} + +[[nodiscard]] inline auto ion_has_superlevel(const int element, const int ion) -> bool { + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + return (get_nlevels(element, ion) > get_nlevels_nlte(element, ion) + 1); +} + +[[nodiscard]] inline auto get_ndowntrans(const int element, const int ion, const int level) -> int +// the number of downward bound-bound transitions from the specified level +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + return globals::elements[element].ions[ion].levels[level].ndowntrans; +} + +[[nodiscard]] inline auto get_nuptrans(const int element, const int ion, const int level) -> int +// the number of upward bound-bound transitions from the specified level +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + return globals::elements[element].ions[ion].levels[level].nuptrans; +} + +inline void set_ndowntrans(const int element, const int ion, const int level, const int ndowntrans) +// the number of downward bound-bound transitions from the specified level +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + globals::elements[element].ions[ion].levels[level].ndowntrans = ndowntrans; +} + +inline void set_nuptrans(const int element, const int ion, const int level, const int nuptrans) +// the number of upward bound-bound transitions from the specified level +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + globals::elements[element].ions[ion].levels[level].nuptrans = nuptrans; +} + +[[nodiscard]] inline auto get_phixtargetindex(const int element, const int ion, const int level, + const int upperionlevel) -> int { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { + if (upperionlevel == get_phixsupperlevel(element, ion, level, phixstargetindex)) { + return phixstargetindex; + } + } + printout("Could not find phixstargetindex\n"); + std::abort(); + return -1; +} + +[[nodiscard]] inline auto get_emtype_continuum(const int element, const int ion, const int level, + const int upperionlevel) -> int +// Returns the emissiontype index of the continuum associated to the given level. Will be negative and ordered by +// element/ion/level/phixstargetindex +{ + const int phixstargetindex = get_phixtargetindex(element, ion, level, upperionlevel); + return globals::elements[element].ions[ion].levels[level].cont_index - phixstargetindex; +} -auto get_continuumindex_phixstargetindex(int element, int ion, int level, int phixstargetindex) -> int; -auto get_continuumindex(int element, int ion, int level, int upperionlevel) -> int; -auto get_phixtargetindex(int element, int ion, int level, int upperionlevel) -> int; -auto get_tau_sobolev(int modelgridindex, int lineindex, double t_current) -> double; -auto get_nnion_tot(int modelgridindex) -> double; -auto is_nlte(int element, int ion, int level) -> bool; -auto level_isinsuperlevel(int element, int ion, int level) -> bool; -auto photoionization_crosssection_fromtable(const float *photoion_xs, double nu_edge, double nu) -> double; -void set_nelements(int nelements_in); -auto get_nelements() -> int; -auto get_atomicnumber(int element) -> int; -auto get_elementindex(int Z) -> int; -auto get_includedions() -> int; -void update_includedions_maxnions(); -auto get_max_nions() -> int; -auto get_nions(int element) -> int; -auto get_ionstage(int element, int ion) -> int; -auto get_nlevels(int element, int ion) -> int; -auto get_nlevels_nlte(int element, int ion) -> int; -auto get_nlevels_groundterm(int element, int ion) -> int; -auto get_ionisinglevels(int element, int ion) -> int; -auto get_uniqueionindex(int element, int ion) -> int; -[[nodiscard]] auto get_ionfromuniqueionindex(int allionsindex) -> std::tuple; -auto get_uniquelevelindex(int element, int ion, int level) -> int; -void get_levelfromuniquelevelindex(int alllevelsindex, int *element, int *ion, int *level); -auto epsilon(int element, int ion, int level) -> double; -auto stat_weight(int element, int ion, int level) -> double; -auto get_maxrecombininglevel(int element, int ion) -> int; -auto ion_has_superlevel(int element, int ion) -> bool; -auto get_ndowntrans(int element, int ion, int level) -> int; -auto get_nuptrans(int element, int ion, int level) -> int; -auto get_nphixstargets(int element, int ion, int level) -> int; -auto get_phixsupperlevel(int element, int ion, int level, int phixstargetindex) -> int; -auto get_phixsprobability(int element, int ion, int level, int phixstargetindex) -> double; -void set_ndowntrans(int element, int ion, int level, int ndowntrans); -void set_nuptrans(int element, int ion, int level, int nuptrans); -auto einstein_spontaneous_emission(int lineindex) -> double; -auto photoionization_crosssection(int element, int ion, int level, double nu_edge, double nu) -> double; -auto get_phixs_threshold(int element, int ion, int level, int phixstargetindex) -> double; -auto elem_has_nlte_levels(int element) -> bool; -auto elem_has_nlte_levels_search(int element) -> bool; +[[nodiscard]] inline auto get_phixs_threshold(const int element, const int ion, const int level, + const int phixstargetindex) -> double +/// Returns the energy of (element,ion,level). +{ + assert_testmodeonly(element < get_nelements()); + assert_testmodeonly(ion < get_nions(element)); + assert_testmodeonly(level < get_nlevels(element, ion)); + assert_testmodeonly(phixstargetindex < get_nphixstargets(element, ion, level)); + const int upperlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); + const double E_threshold = epsilon(element, ion + 1, upperlevel) - epsilon(element, ion, level); + return E_threshold; +} #endif // ATOMIC_H diff --git a/constants.h b/constants.h index fc1f7e2df..eec782f97 100644 --- a/constants.h +++ b/constants.h @@ -1,32 +1,38 @@ +#pragma once #ifndef CONSTANTS_H #define CONSTANTS_H -#include +#include +#include -/// fundamental constants -constexpr double CLIGHT = 2.99792458e+10; /// Speed of light [cm/s] +// 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 // can be changed for testing. -constexpr double H = 6.6260755e-27; /// Planck constant [erg s] -constexpr double MSUN = 1.98855e+33; /// Solar mass [g] -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 = 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] -constexpr double SIGMA_T = 6.6524e-25; /// Thomson cross-section -constexpr double THOMSON_LIMIT = 1e-2; /// Limit below which e-scattering is Thomson -constexpr double PARSEC = 3.0857e+18; /// pc to cm [cm/pc] -constexpr double KB = 1.38064852e-16; /// Boltzmann constant [erg/K] -constexpr double STEBO = 5.670400e-5; /// Stefan-Boltzmann constant [erg cm^−2 s^−1 K^−4.] - /// (data from NIST http://physics.nist.gov/cgi-bin/cuu/Value?eqsigma) -constexpr double SAHACONST = 2.0706659e-16; /// Saha constant - -/// numerical constants -constexpr double CLIGHTSQUARED = 8.9875518e+20; /// Speed of light squared [cm^2/s^2] +constexpr double H = 6.6260755e-27; // Planck constant [erg s] +constexpr double MSUN = 1.98855e+33; // Solar mass [g] +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 = std::numbers::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.; // day to seconds [s/day] +constexpr double SIGMA_T = 6.6524e-25; // Thomson cross-section [cm2] +constexpr double THOMSON_LIMIT = 1e-2; // Limit below which e-scattering is Thomson +constexpr double PARSEC = 3.0857e+18; // pc to cm [cm/pc] +constexpr double KB = 1.38064852e-16; // Boltzmann constant [erg/K] +constexpr double STEBO = 5.670400e-5; // Stefan-Boltzmann constant [erg cm^−2 s^−1 K^−4.] + // (data from NIST http://physics.nist.gov/cgi-bin/cuu/Value?eqsigma) +constexpr double SAHACONST = 2.0706659e-16; // Saha constant + +constexpr double EULERGAMMA = std::numbers::egamma; + +// numerical constants + +constexpr double CLIGHTSQUARED = 8.9875518e+20; // Speed of light squared [cm^2/s^2] constexpr double TWOOVERCLIGHTSQUARED = 2.2253001e-21; constexpr double TWOHOVERCLIGHTSQUARED = 1.4745007e-47; constexpr double CLIGHTSQUAREDOVERTWOH = 6.7819570e+46; @@ -45,6 +51,7 @@ enum gridtypes { 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. diff --git a/data/xcom_photoion_data.txt b/data/xcom_photoion_data.txt new file mode 100644 index 000000000..0ef5730ce --- /dev/null +++ b/data/xcom_photoion_data.txt @@ -0,0 +1,5210 @@ +# Photoionization data from the XCOM database by NIST (https://www.nist.gov/pml/xcom-photon-cross-sections-database, Berger et al. 2010) + 1 1.000000000000E-03 1.141000000000E+01 + 1 1.500000000000E-03 2.932000000000E+00 + 1 2.000000000000E-03 1.111000000000E+00 + 1 3.000000000000E-03 2.806000000000E-01 + 1 4.000000000000E-03 1.053000000000E-01 + 1 5.000000000000E-03 4.907000000000E-02 + 1 6.000000000000E-03 2.627000000000E-02 + 1 8.000000000000E-03 9.816000000000E-03 + 1 1.000000000000E-02 4.558000000000E-03 + 1 1.500000000000E-02 1.128000000000E-03 + 1 2.000000000000E-02 4.182000000000E-04 + 1 3.000000000000E-02 1.032000000000E-04 + 1 4.000000000000E-02 3.823000000000E-05 + 1 5.000000000000E-02 1.771000000000E-05 + 1 6.000000000000E-02 9.455000000000E-06 + 1 8.000000000000E-02 3.523000000000E-06 + 1 1.000000000000E-01 1.644000000000E-06 + 1 1.500000000000E-01 4.176000000000E-07 + 1 2.000000000000E-01 1.611000000000E-07 + 1 3.000000000000E-01 4.414000000000E-08 + 1 4.000000000000E-01 1.858000000000E-08 + 1 5.000000000000E-01 9.927000000000E-09 + 1 6.000000000000E-01 6.158000000000E-09 + 1 8.000000000000E-01 3.118000000000E-09 + 1 1.000000000000E+00 1.961000000000E-09 + 1 1.022000000000E+00 1.880000000000E-09 + 1 1.250000000000E+00 1.304000000000E-09 + 1 1.500000000000E+00 9.732000000000E-10 + 1 2.000000000000E+00 6.201000000000E-10 + 1 2.044000000000E+00 6.004000000000E-10 + 1 3.000000000000E+00 3.524000000000E-10 + 1 4.000000000000E+00 2.444000000000E-10 + 1 5.000000000000E+00 1.866000000000E-10 + 1 6.000000000000E+00 1.508000000000E-10 + 1 7.000000000000E+00 1.264000000000E-10 + 1 8.000000000000E+00 1.088000000000E-10 + 1 9.000000000000E+00 9.546000000000E-11 + 1 1.000000000000E+01 8.503000000000E-11 + 2 1.000000000000E-03 4.017000000000E+02 + 2 1.500000000000E-03 1.089000000000E+02 + 2 2.000000000000E-03 4.323000000000E+01 + 2 3.000000000000E-03 1.117000000000E+01 + 2 4.000000000000E-03 4.234000000000E+00 + 2 5.000000000000E-03 2.026000000000E+00 + 2 6.000000000000E-03 1.099000000000E+00 + 2 8.000000000000E-03 4.110000000000E-01 + 2 1.000000000000E-02 1.941000000000E-01 + 2 1.500000000000E-02 4.875000000000E-02 + 2 2.000000000000E-02 1.829000000000E-02 + 2 3.000000000000E-02 4.568000000000E-03 + 2 4.000000000000E-02 1.705000000000E-03 + 2 5.000000000000E-02 7.945000000000E-04 + 2 6.000000000000E-02 4.259000000000E-04 + 2 8.000000000000E-02 1.596000000000E-04 + 2 1.000000000000E-01 7.484000000000E-05 + 2 1.500000000000E-01 1.916000000000E-05 + 2 2.000000000000E-01 7.428000000000E-06 + 2 3.000000000000E-01 2.049000000000E-06 + 2 4.000000000000E-01 8.662000000000E-07 + 2 5.000000000000E-01 4.635000000000E-07 + 2 6.000000000000E-01 2.876000000000E-07 + 2 8.000000000000E-01 1.449000000000E-07 + 2 1.000000000000E+00 9.028000000000E-08 + 2 1.022000000000E+00 8.294000000000E-08 + 2 1.250000000000E+00 5.662000000000E-08 + 2 1.500000000000E+00 4.140000000000E-08 + 2 2.000000000000E+00 2.632000000000E-08 + 2 2.044000000000E+00 2.549000000000E-08 + 2 3.000000000000E+00 1.492000000000E-08 + 2 4.000000000000E+00 1.033000000000E-08 + 2 5.000000000000E+00 7.879000000000E-09 + 2 6.000000000000E+00 6.361000000000E-09 + 2 7.000000000000E+00 5.330000000000E-09 + 2 8.000000000000E+00 4.585000000000E-09 + 2 9.000000000000E+00 4.022000000000E-09 + 2 1.000000000000E+01 3.581000000000E-09 + 3 1.000000000000E-03 2.690000000000E+03 + 3 1.500000000000E-03 7.641000000000E+02 + 3 2.000000000000E-03 3.080000000000E+02 + 3 3.000000000000E-03 8.354000000000E+01 + 3 4.000000000000E-03 3.273000000000E+01 + 3 5.000000000000E-03 1.571000000000E+01 + 3 6.000000000000E-03 8.604000000000E+00 + 3 8.000000000000E-03 3.305000000000E+00 + 3 1.000000000000E-02 1.568000000000E+00 + 3 1.500000000000E-02 4.009000000000E-01 + 3 2.000000000000E-02 1.516000000000E-01 + 3 3.000000000000E-02 3.841000000000E-02 + 3 4.000000000000E-02 1.445000000000E-02 + 3 5.000000000000E-02 6.770000000000E-03 + 3 6.000000000000E-02 3.645000000000E-03 + 3 8.000000000000E-02 1.373000000000E-03 + 3 1.000000000000E-01 6.464000000000E-04 + 3 1.500000000000E-01 1.666000000000E-04 + 3 2.000000000000E-01 6.481000000000E-05 + 3 3.000000000000E-01 1.796000000000E-05 + 3 4.000000000000E-01 7.615000000000E-06 + 3 5.000000000000E-01 4.081000000000E-06 + 3 6.000000000000E-01 2.533000000000E-06 + 3 8.000000000000E-01 1.276000000000E-06 + 3 1.000000000000E+00 7.939000000000E-07 + 3 1.022000000000E+00 7.038000000000E-07 + 3 1.250000000000E+00 4.788000000000E-07 + 3 1.500000000000E+00 3.486000000000E-07 + 3 2.000000000000E+00 2.212000000000E-07 + 3 2.044000000000E+00 2.141000000000E-07 + 3 3.000000000000E+00 1.250000000000E-07 + 3 4.000000000000E+00 8.643000000000E-08 + 3 5.000000000000E+00 6.586000000000E-08 + 3 6.000000000000E+00 5.313000000000E-08 + 3 7.000000000000E+00 4.450000000000E-08 + 3 8.000000000000E+00 3.826000000000E-08 + 3 9.000000000000E+00 3.355000000000E-08 + 3 1.000000000000E+01 2.987000000000E-08 + 4 1.000000000000E-03 9.032000000000E+03 + 4 1.500000000000E-03 2.681000000000E+03 + 4 2.000000000000E-03 1.111000000000E+03 + 4 3.000000000000E-03 3.128000000000E+02 + 4 4.000000000000E-03 1.252000000000E+02 + 4 5.000000000000E-03 6.107000000000E+01 + 4 6.000000000000E-03 3.381000000000E+01 + 4 8.000000000000E-03 1.320000000000E+01 + 4 1.000000000000E-02 6.329000000000E+00 + 4 1.500000000000E-02 1.647000000000E+00 + 4 2.000000000000E-02 6.296000000000E-01 + 4 3.000000000000E-02 1.611000000000E-01 + 4 4.000000000000E-02 6.106000000000E-02 + 4 5.000000000000E-02 2.872000000000E-02 + 4 6.000000000000E-02 1.555000000000E-02 + 4 8.000000000000E-02 5.891000000000E-03 + 4 1.000000000000E-01 2.782000000000E-03 + 4 1.500000000000E-01 7.210000000000E-04 + 4 2.000000000000E-01 2.815000000000E-04 + 4 3.000000000000E-01 7.833000000000E-05 + 4 4.000000000000E-01 3.328000000000E-05 + 4 5.000000000000E-01 1.786000000000E-05 + 4 6.000000000000E-01 1.109000000000E-05 + 4 8.000000000000E-01 5.591000000000E-06 + 4 1.000000000000E+00 3.476000000000E-06 + 4 1.022000000000E+00 3.144000000000E-06 + 4 1.250000000000E+00 2.137000000000E-06 + 4 1.500000000000E+00 1.555000000000E-06 + 4 2.000000000000E+00 9.844000000000E-07 + 4 2.044000000000E+00 9.528000000000E-07 + 4 3.000000000000E+00 5.550000000000E-07 + 4 4.000000000000E+00 3.831000000000E-07 + 4 5.000000000000E+00 2.916000000000E-07 + 4 6.000000000000E+00 2.351000000000E-07 + 4 7.000000000000E+00 1.968000000000E-07 + 4 8.000000000000E+00 1.691000000000E-07 + 4 9.000000000000E+00 1.482000000000E-07 + 4 1.000000000000E+01 1.319000000000E-07 + 5 1.000000000000E-03 2.204000000000E+04 + 5 1.500000000000E-03 6.749000000000E+03 + 5 2.000000000000E-03 2.856000000000E+03 + 5 3.000000000000E-03 8.291000000000E+02 + 5 4.000000000000E-03 3.386000000000E+02 + 5 5.000000000000E-03 1.675000000000E+02 + 5 6.000000000000E-03 9.375000000000E+01 + 5 8.000000000000E-03 3.717000000000E+01 + 5 1.000000000000E-02 1.801000000000E+01 + 5 1.500000000000E-02 4.766000000000E+00 + 5 2.000000000000E-02 1.840000000000E+00 + 5 3.000000000000E-02 4.768000000000E-01 + 5 4.000000000000E-02 1.820000000000E-01 + 5 5.000000000000E-02 8.606000000000E-02 + 5 6.000000000000E-02 4.665000000000E-02 + 5 8.000000000000E-02 1.775000000000E-02 + 5 1.000000000000E-01 8.432000000000E-03 + 5 1.500000000000E-01 2.197000000000E-03 + 5 2.000000000000E-01 8.606000000000E-04 + 5 3.000000000000E-01 2.404000000000E-04 + 5 4.000000000000E-01 1.023000000000E-04 + 5 5.000000000000E-01 5.497000000000E-05 + 5 6.000000000000E-01 3.417000000000E-05 + 5 8.000000000000E-01 1.722000000000E-05 + 5 1.000000000000E+00 1.071000000000E-05 + 5 1.022000000000E+00 9.792000000000E-06 + 5 1.250000000000E+00 6.653000000000E-06 + 5 1.500000000000E+00 4.835000000000E-06 + 5 2.000000000000E+00 3.056000000000E-06 + 5 2.044000000000E+00 2.957000000000E-06 + 5 3.000000000000E+00 1.719000000000E-06 + 5 4.000000000000E+00 1.184000000000E-06 + 5 5.000000000000E+00 9.007000000000E-07 + 5 6.000000000000E+00 7.256000000000E-07 + 5 7.000000000000E+00 6.070000000000E-07 + 5 8.000000000000E+00 5.215000000000E-07 + 5 9.000000000000E+00 4.570000000000E-07 + 5 1.000000000000E+01 4.067000000000E-07 + 6 1.000000000000E-03 4.407000000000E+04 + 6 1.500000000000E-03 1.395000000000E+04 + 6 2.000000000000E-03 6.017000000000E+03 + 6 3.000000000000E-03 1.788000000000E+03 + 6 4.000000000000E-03 7.427000000000E+02 + 6 5.000000000000E-03 3.721000000000E+02 + 6 6.000000000000E-03 2.103000000000E+02 + 6 8.000000000000E-03 8.459000000000E+01 + 6 1.000000000000E-02 4.140000000000E+01 + 6 1.500000000000E-02 1.114000000000E+01 + 6 2.000000000000E-02 4.341000000000E+00 + 6 3.000000000000E-02 1.138000000000E+00 + 6 4.000000000000E-02 4.374000000000E-01 + 6 5.000000000000E-02 2.079000000000E-01 + 6 6.000000000000E-02 1.131000000000E-01 + 6 8.000000000000E-02 4.327000000000E-02 + 6 1.000000000000E-01 2.057000000000E-02 + 6 1.500000000000E-01 5.397000000000E-03 + 6 2.000000000000E-01 2.121000000000E-03 + 6 3.000000000000E-01 5.944000000000E-04 + 6 4.000000000000E-01 2.536000000000E-04 + 6 5.000000000000E-01 1.364000000000E-04 + 6 6.000000000000E-01 8.482000000000E-05 + 6 8.000000000000E-01 4.277000000000E-05 + 6 1.000000000000E+00 2.659000000000E-05 + 6 1.022000000000E+00 2.452000000000E-05 + 6 1.250000000000E+00 1.665000000000E-05 + 6 1.500000000000E+00 1.209000000000E-05 + 6 2.000000000000E+00 7.631000000000E-06 + 6 2.044000000000E+00 7.384000000000E-06 + 6 3.000000000000E+00 4.282000000000E-06 + 6 4.000000000000E+00 2.947000000000E-06 + 6 5.000000000000E+00 2.239000000000E-06 + 6 6.000000000000E+00 1.802000000000E-06 + 6 7.000000000000E+00 1.507000000000E-06 + 6 8.000000000000E+00 1.294000000000E-06 + 6 9.000000000000E+00 1.134000000000E-06 + 6 1.000000000000E+01 1.009000000000E-06 + 7 1.000000000000E-03 7.699000000000E+04 + 7 1.500000000000E-03 2.515000000000E+04 + 7 2.000000000000E-03 1.107000000000E+04 + 7 3.000000000000E-03 3.367000000000E+03 + 7 4.000000000000E-03 1.418000000000E+03 + 7 5.000000000000E-03 7.180000000000E+02 + 7 6.000000000000E-03 4.092000000000E+02 + 7 8.000000000000E-03 1.667000000000E+02 + 7 1.000000000000E-02 8.241000000000E+01 + 7 1.500000000000E-02 2.250000000000E+01 + 7 2.000000000000E-02 8.856000000000E+00 + 7 3.000000000000E-02 2.348000000000E+00 + 7 4.000000000000E-02 9.087000000000E-01 + 7 5.000000000000E-02 4.340000000000E-01 + 7 6.000000000000E-02 2.369000000000E-01 + 7 8.000000000000E-02 9.114000000000E-02 + 7 1.000000000000E-01 4.347000000000E-02 + 7 1.500000000000E-01 1.144000000000E-02 + 7 2.000000000000E-01 4.516000000000E-03 + 7 3.000000000000E-01 1.270000000000E-03 + 7 4.000000000000E-01 5.428000000000E-04 + 7 5.000000000000E-01 2.922000000000E-04 + 7 6.000000000000E-01 1.819000000000E-04 + 7 8.000000000000E-01 9.177000000000E-05 + 7 1.000000000000E+00 5.704000000000E-05 + 7 1.022000000000E+00 5.293000000000E-05 + 7 1.250000000000E+00 3.593000000000E-05 + 7 1.500000000000E+00 2.609000000000E-05 + 7 2.000000000000E+00 1.643000000000E-05 + 7 2.044000000000E+00 1.590000000000E-05 + 7 3.000000000000E+00 9.200000000000E-06 + 7 4.000000000000E+00 6.323000000000E-06 + 7 5.000000000000E+00 4.799000000000E-06 + 7 6.000000000000E+00 3.861000000000E-06 + 7 7.000000000000E+00 3.227000000000E-06 + 7 8.000000000000E+00 2.770000000000E-06 + 7 9.000000000000E+00 2.426000000000E-06 + 7 1.000000000000E+01 2.157000000000E-06 + 8 1.000000000000E-03 1.219000000000E+05 + 8 1.500000000000E-03 4.111000000000E+04 + 8 2.000000000000E-03 1.843000000000E+04 + 8 3.000000000000E-03 5.739000000000E+03 + 8 4.000000000000E-03 2.452000000000E+03 + 8 5.000000000000E-03 1.254000000000E+03 + 8 6.000000000000E-03 7.199000000000E+02 + 8 8.000000000000E-03 2.967000000000E+02 + 8 1.000000000000E-02 1.479000000000E+02 + 8 1.500000000000E-02 4.096000000000E+01 + 8 2.000000000000E-02 1.627000000000E+01 + 8 3.000000000000E-02 4.360000000000E+00 + 8 4.000000000000E-02 1.699000000000E+00 + 8 5.000000000000E-02 8.151000000000E-01 + 8 6.000000000000E-02 4.465000000000E-01 + 8 8.000000000000E-02 1.726000000000E-01 + 8 1.000000000000E-01 8.264000000000E-02 + 8 1.500000000000E-01 2.186000000000E-02 + 8 2.000000000000E-01 8.637000000000E-03 + 8 3.000000000000E-01 2.441000000000E-03 + 8 4.000000000000E-01 1.045000000000E-03 + 8 5.000000000000E-01 5.634000000000E-04 + 8 6.000000000000E-01 3.509000000000E-04 + 8 8.000000000000E-01 1.771000000000E-04 + 8 1.000000000000E+00 1.101000000000E-04 + 8 1.022000000000E+00 1.026000000000E-04 + 8 1.250000000000E+00 6.966000000000E-05 + 8 1.500000000000E+00 5.054000000000E-05 + 8 2.000000000000E+00 3.179000000000E-05 + 8 2.044000000000E+00 3.075000000000E-05 + 8 3.000000000000E+00 1.776000000000E-05 + 8 4.000000000000E+00 1.219000000000E-05 + 8 5.000000000000E+00 9.242000000000E-06 + 8 6.000000000000E+00 7.430000000000E-06 + 8 7.000000000000E+00 6.207000000000E-06 + 8 8.000000000000E+00 5.326000000000E-06 + 8 9.000000000000E+00 4.663000000000E-06 + 8 1.000000000000E+01 4.146000000000E-06 + 9 1.000000000000E-03 1.782000000000E+05 + 9 1.500000000000E-03 6.237000000000E+04 + 9 2.000000000000E-03 2.850000000000E+04 + 9 3.000000000000E-03 9.073000000000E+03 + 9 4.000000000000E-03 3.931000000000E+03 + 9 5.000000000000E-03 2.029000000000E+03 + 9 6.000000000000E-03 1.174000000000E+03 + 9 8.000000000000E-03 4.888000000000E+02 + 9 1.000000000000E-02 2.455000000000E+02 + 9 1.500000000000E-02 6.890000000000E+01 + 9 2.000000000000E-02 2.760000000000E+01 + 9 3.000000000000E-02 7.476000000000E+00 + 9 4.000000000000E-02 2.933000000000E+00 + 9 5.000000000000E-02 1.414000000000E+00 + 9 6.000000000000E-02 7.769000000000E-01 + 9 8.000000000000E-02 3.017000000000E-01 + 9 1.000000000000E-01 1.449000000000E-01 + 9 1.500000000000E-01 3.854000000000E-02 + 9 2.000000000000E-01 1.527000000000E-02 + 9 3.000000000000E-01 4.330000000000E-03 + 9 4.000000000000E-01 1.858000000000E-03 + 9 5.000000000000E-01 1.002000000000E-03 + 9 6.000000000000E-01 6.245000000000E-04 + 9 8.000000000000E-01 3.153000000000E-04 + 9 1.000000000000E+00 1.960000000000E-04 + 9 1.022000000000E+00 1.834000000000E-04 + 9 1.250000000000E+00 1.244000000000E-04 + 9 1.500000000000E+00 9.028000000000E-05 + 9 2.000000000000E+00 5.669000000000E-05 + 9 2.044000000000E+00 5.483000000000E-05 + 9 3.000000000000E+00 3.160000000000E-05 + 9 4.000000000000E+00 2.166000000000E-05 + 9 5.000000000000E+00 1.641000000000E-05 + 9 6.000000000000E+00 1.319000000000E-05 + 9 7.000000000000E+00 1.101000000000E-05 + 9 8.000000000000E+00 9.444000000000E-06 + 9 9.000000000000E+00 8.266000000000E-06 + 9 1.000000000000E+01 7.348000000000E-06 + 10 1.000000000000E-03 2.482000000000E+05 + 10 1.500000000000E-03 8.928000000000E+04 + 10 2.000000000000E-03 4.159000000000E+04 + 10 3.000000000000E-03 1.353000000000E+04 + 10 4.000000000000E-03 5.939000000000E+03 + 10 5.000000000000E-03 3.095000000000E+03 + 10 6.000000000000E-03 1.804000000000E+03 + 10 8.000000000000E-03 7.586000000000E+02 + 10 1.000000000000E-02 3.837000000000E+02 + 10 1.500000000000E-02 1.090000000000E+02 + 10 2.000000000000E-02 4.401000000000E+01 + 10 3.000000000000E-02 1.204000000000E+01 + 10 4.000000000000E-02 4.753000000000E+00 + 10 5.000000000000E-02 2.301000000000E+00 + 10 6.000000000000E-02 1.269000000000E+00 + 10 8.000000000000E-02 4.952000000000E-01 + 10 1.000000000000E-01 2.386000000000E-01 + 10 1.500000000000E-01 6.378000000000E-02 + 10 2.000000000000E-01 2.536000000000E-02 + 10 3.000000000000E-01 7.201000000000E-03 + 10 4.000000000000E-01 3.099000000000E-03 + 10 5.000000000000E-01 1.674000000000E-03 + 10 6.000000000000E-01 1.043000000000E-03 + 10 8.000000000000E-01 5.271000000000E-04 + 10 1.000000000000E+00 3.277000000000E-04 + 10 1.022000000000E+00 3.074000000000E-04 + 10 1.250000000000E+00 2.086000000000E-04 + 10 1.500000000000E+00 1.512000000000E-04 + 10 2.000000000000E+00 9.481000000000E-05 + 10 2.044000000000E+00 9.169000000000E-05 + 10 3.000000000000E+00 5.274000000000E-05 + 10 4.000000000000E+00 3.611000000000E-05 + 10 5.000000000000E+00 2.733000000000E-05 + 10 6.000000000000E+00 2.195000000000E-05 + 10 7.000000000000E+00 1.832000000000E-05 + 10 8.000000000000E+00 1.571000000000E-05 + 10 9.000000000000E+00 1.374000000000E-05 + 10 1.000000000000E+01 1.221000000000E-05 + 11 1.000000000000E-03 2.490000000000E+04 + 11 1.035000000000E-03 2.267000000000E+04 + 11 1.072000000000E-03 2.066000000000E+04 + 11 1.072000100000E-03 2.456000000000E+05 + 11 1.500000000000E-03 1.219000000000E+05 + 11 2.000000000000E-03 5.799000000000E+04 + 11 3.000000000000E-03 1.930000000000E+04 + 11 4.000000000000E-03 8.586000000000E+03 + 11 5.000000000000E-03 4.516000000000E+03 + 11 6.000000000000E-03 2.649000000000E+03 + 11 8.000000000000E-03 1.126000000000E+03 + 11 1.000000000000E-02 5.735000000000E+02 + 11 1.500000000000E-02 1.648000000000E+02 + 11 2.000000000000E-02 6.705000000000E+01 + 11 3.000000000000E-02 1.853000000000E+01 + 11 4.000000000000E-02 7.358000000000E+00 + 11 5.000000000000E-02 3.577000000000E+00 + 11 6.000000000000E-02 1.980000000000E+00 + 11 8.000000000000E-02 7.759000000000E-01 + 11 1.000000000000E-01 3.751000000000E-01 + 11 1.500000000000E-01 1.007000000000E-01 + 11 2.000000000000E-01 4.018000000000E-02 + 11 3.000000000000E-01 1.145000000000E-02 + 11 4.000000000000E-01 4.933000000000E-03 + 11 5.000000000000E-01 2.668000000000E-03 + 11 6.000000000000E-01 1.664000000000E-03 + 11 8.000000000000E-01 8.411000000000E-04 + 11 1.000000000000E+00 5.228000000000E-04 + 11 1.022000000000E+00 4.914000000000E-04 + 11 1.250000000000E+00 3.333000000000E-04 + 11 1.500000000000E+00 2.416000000000E-04 + 11 2.000000000000E+00 1.513000000000E-04 + 11 2.044000000000E+00 1.463000000000E-04 + 11 3.000000000000E+00 8.400000000000E-05 + 11 4.000000000000E+00 5.743000000000E-05 + 11 5.000000000000E+00 4.344000000000E-05 + 11 6.000000000000E+00 3.486000000000E-05 + 11 7.000000000000E+00 2.908000000000E-05 + 11 8.000000000000E+00 2.493000000000E-05 + 11 9.000000000000E+00 2.181000000000E-05 + 11 1.000000000000E+01 1.937000000000E-05 + 12 1.000000000000E-03 3.714000000000E+04 + 12 1.142000000000E-03 2.600000000000E+04 + 12 1.305000000000E-03 1.820000000000E+04 + 12 1.305000100000E-03 2.196000000000E+05 + 12 1.500000000000E-03 1.615000000000E+05 + 12 2.000000000000E-03 7.789000000000E+04 + 12 3.000000000000E-03 2.651000000000E+04 + 12 4.000000000000E-03 1.195000000000E+04 + 12 5.000000000000E-03 6.341000000000E+03 + 12 6.000000000000E-03 3.745000000000E+03 + 12 8.000000000000E-03 1.607000000000E+03 + 12 1.000000000000E-02 8.247000000000E+02 + 12 1.500000000000E-02 2.396000000000E+02 + 12 2.000000000000E-02 9.819000000000E+01 + 12 3.000000000000E-02 2.738000000000E+01 + 12 4.000000000000E-02 1.094000000000E+01 + 12 5.000000000000E-02 5.342000000000E+00 + 12 6.000000000000E-02 2.965000000000E+00 + 12 8.000000000000E-02 1.168000000000E+00 + 12 1.000000000000E-01 5.661000000000E-01 + 12 1.500000000000E-01 1.528000000000E-01 + 12 2.000000000000E-01 6.112000000000E-02 + 12 3.000000000000E-01 1.747000000000E-02 + 12 4.000000000000E-01 7.534000000000E-03 + 12 5.000000000000E-01 4.082000000000E-03 + 12 6.000000000000E-01 2.549000000000E-03 + 12 8.000000000000E-01 1.288000000000E-03 + 12 1.000000000000E+00 8.010000000000E-04 + 12 1.022000000000E+00 7.538000000000E-04 + 12 1.250000000000E+00 5.111000000000E-04 + 12 1.500000000000E+00 3.704000000000E-04 + 12 2.000000000000E+00 2.316000000000E-04 + 12 2.044000000000E+00 2.239000000000E-04 + 12 3.000000000000E+00 1.284000000000E-04 + 12 4.000000000000E+00 8.766000000000E-05 + 12 5.000000000000E+00 6.625000000000E-05 + 12 6.000000000000E+00 5.313000000000E-05 + 12 7.000000000000E+00 4.430000000000E-05 + 12 8.000000000000E+00 3.796000000000E-05 + 12 9.000000000000E+00 3.320000000000E-05 + 12 1.000000000000E+01 2.949000000000E-05 + 13 1.000000000000E-03 5.300000000000E+04 + 13 1.500000000000E-03 1.793000000000E+04 + 13 1.560000000000E-03 1.613000000000E+04 + 13 1.560000100000E-03 1.772000000000E+05 + 13 2.000000000000E-03 1.013000000000E+05 + 13 3.000000000000E-03 3.524000000000E+04 + 13 4.000000000000E-03 1.609000000000E+04 + 13 5.000000000000E-03 8.612000000000E+03 + 13 6.000000000000E-03 5.119000000000E+03 + 13 8.000000000000E-03 2.218000000000E+03 + 13 1.000000000000E-02 1.145000000000E+03 + 13 1.500000000000E-02 3.367000000000E+02 + 13 2.000000000000E-02 1.389000000000E+02 + 13 3.000000000000E-02 3.908000000000E+01 + 13 4.000000000000E-02 1.570000000000E+01 + 13 5.000000000000E-02 7.696000000000E+00 + 13 6.000000000000E-02 4.285000000000E+00 + 13 8.000000000000E-02 1.695000000000E+00 + 13 1.000000000000E-01 8.245000000000E-01 + 13 1.500000000000E-01 2.237000000000E-01 + 13 2.000000000000E-01 8.970000000000E-02 + 13 3.000000000000E-01 2.573000000000E-02 + 13 4.000000000000E-01 1.111000000000E-02 + 13 5.000000000000E-01 6.021000000000E-03 + 13 6.000000000000E-01 3.764000000000E-03 + 13 8.000000000000E-01 1.905000000000E-03 + 13 1.000000000000E+00 1.184000000000E-03 + 13 1.022000000000E+00 1.115000000000E-03 + 13 1.250000000000E+00 7.562000000000E-04 + 13 1.500000000000E+00 5.476000000000E-04 + 13 2.000000000000E+00 3.420000000000E-04 + 13 2.044000000000E+00 3.307000000000E-04 + 13 3.000000000000E+00 1.892000000000E-04 + 13 4.000000000000E+00 1.291000000000E-04 + 13 5.000000000000E+00 9.746000000000E-05 + 13 6.000000000000E+00 7.812000000000E-05 + 13 7.000000000000E+00 6.511000000000E-05 + 13 8.000000000000E+00 5.577000000000E-05 + 13 9.000000000000E+00 4.876000000000E-05 + 13 1.000000000000E+01 4.330000000000E-05 + 14 1.000000000000E-03 7.310000000000E+04 + 14 1.500000000000E-03 2.487000000000E+04 + 14 1.839000000000E-03 1.432000000000E+04 + 14 1.839000100000E-03 1.488000000000E+05 + 14 2.000000000000E-03 1.294000000000E+05 + 14 3.000000000000E-03 4.555000000000E+04 + 14 4.000000000000E-03 2.105000000000E+04 + 14 5.000000000000E-03 1.137000000000E+04 + 14 6.000000000000E-03 6.802000000000E+03 + 14 8.000000000000E-03 2.975000000000E+03 + 14 1.000000000000E-02 1.546000000000E+03 + 14 1.500000000000E-02 4.593000000000E+02 + 14 2.000000000000E-02 1.907000000000E+02 + 14 3.000000000000E-02 5.413000000000E+01 + 14 4.000000000000E-02 2.186000000000E+01 + 14 5.000000000000E-02 1.076000000000E+01 + 14 6.000000000000E-02 6.009000000000E+00 + 14 8.000000000000E-02 2.388000000000E+00 + 14 1.000000000000E-01 1.165000000000E+00 + 14 1.500000000000E-01 3.174000000000E-01 + 14 2.000000000000E-01 1.276000000000E-01 + 14 3.000000000000E-01 3.676000000000E-02 + 14 4.000000000000E-01 1.590000000000E-02 + 14 5.000000000000E-01 8.624000000000E-03 + 14 6.000000000000E-01 5.393000000000E-03 + 14 8.000000000000E-01 2.730000000000E-03 + 14 1.000000000000E+00 1.697000000000E-03 + 14 1.022000000000E+00 1.601000000000E-03 + 14 1.250000000000E+00 1.085000000000E-03 + 14 1.500000000000E+00 7.853000000000E-04 + 14 2.000000000000E+00 4.899000000000E-04 + 14 2.044000000000E+00 4.736000000000E-04 + 14 3.000000000000E+00 2.705000000000E-04 + 14 4.000000000000E+00 1.843000000000E-04 + 14 5.000000000000E+00 1.391000000000E-04 + 14 6.000000000000E+00 1.114000000000E-04 + 14 7.000000000000E+00 9.283000000000E-05 + 14 8.000000000000E+00 7.950000000000E-05 + 14 9.000000000000E+00 6.948000000000E-05 + 14 1.000000000000E+01 6.169000000000E-05 + 15 1.000000000000E-03 9.824000000000E+04 + 15 1.500000000000E-03 3.355000000000E+04 + 15 2.000000000000E-03 1.541000000000E+04 + 15 2.145000000000E-03 1.272000000000E+04 + 15 2.145000100000E-03 1.271000000000E+05 + 15 3.000000000000E-03 5.741000000000E+04 + 15 4.000000000000E-03 2.688000000000E+04 + 15 5.000000000000E-03 1.465000000000E+04 + 15 6.000000000000E-03 8.819000000000E+03 + 15 8.000000000000E-03 3.892000000000E+03 + 15 1.000000000000E-02 2.037000000000E+03 + 15 1.500000000000E-02 6.112000000000E+02 + 15 2.000000000000E-02 2.556000000000E+02 + 15 3.000000000000E-02 7.310000000000E+01 + 15 4.000000000000E-02 2.968000000000E+01 + 15 5.000000000000E-02 1.467000000000E+01 + 15 6.000000000000E-02 8.213000000000E+00 + 15 8.000000000000E-02 3.276000000000E+00 + 15 1.000000000000E-01 1.603000000000E+00 + 15 1.500000000000E-01 4.389000000000E-01 + 15 2.000000000000E-01 1.770000000000E-01 + 15 3.000000000000E-01 5.113000000000E-02 + 15 4.000000000000E-01 2.216000000000E-02 + 15 5.000000000000E-01 1.203000000000E-02 + 15 6.000000000000E-01 7.523000000000E-03 + 15 8.000000000000E-01 3.813000000000E-03 + 15 1.000000000000E+00 2.370000000000E-03 + 15 1.022000000000E+00 2.237000000000E-03 + 15 1.250000000000E+00 1.516000000000E-03 + 15 1.500000000000E+00 1.097000000000E-03 + 15 2.000000000000E+00 6.837000000000E-04 + 15 2.044000000000E+00 6.609000000000E-04 + 15 3.000000000000E+00 3.769000000000E-04 + 15 4.000000000000E+00 2.566000000000E-04 + 15 5.000000000000E+00 1.934000000000E-04 + 15 6.000000000000E+00 1.549000000000E-04 + 15 7.000000000000E+00 1.290000000000E-04 + 15 8.000000000000E+00 1.104000000000E-04 + 15 9.000000000000E+00 9.649000000000E-05 + 15 1.000000000000E+01 8.565000000000E-05 + 16 1.000000000000E-03 1.292000000000E+05 + 16 1.500000000000E-03 4.427000000000E+04 + 16 2.000000000000E-03 2.038000000000E+04 + 16 2.472000000000E-03 1.143000000000E+04 + 16 2.472000100000E-03 1.101000000000E+05 + 16 3.000000000000E-03 7.117000000000E+04 + 16 4.000000000000E-03 3.366000000000E+04 + 16 5.000000000000E-03 1.849000000000E+04 + 16 6.000000000000E-03 1.120000000000E+04 + 16 8.000000000000E-03 4.986000000000E+03 + 16 1.000000000000E-02 2.625000000000E+03 + 16 1.500000000000E-02 7.958000000000E+02 + 16 2.000000000000E-02 3.349000000000E+02 + 16 3.000000000000E-02 9.657000000000E+01 + 16 4.000000000000E-02 3.942000000000E+01 + 16 5.000000000000E-02 1.954000000000E+01 + 16 6.000000000000E-02 1.097000000000E+01 + 16 8.000000000000E-02 4.396000000000E+00 + 16 1.000000000000E-01 2.157000000000E+00 + 16 1.500000000000E-01 5.934000000000E-01 + 16 2.000000000000E-01 2.400000000000E-01 + 16 3.000000000000E-01 6.954000000000E-02 + 16 4.000000000000E-01 3.020000000000E-02 + 16 5.000000000000E-01 1.641000000000E-02 + 16 6.000000000000E-01 1.027000000000E-02 + 16 8.000000000000E-01 5.206000000000E-03 + 16 1.000000000000E+00 3.236000000000E-03 + 16 1.022000000000E+00 3.057000000000E-03 + 16 1.250000000000E+00 2.071000000000E-03 + 16 1.500000000000E+00 1.499000000000E-03 + 16 2.000000000000E+00 9.326000000000E-04 + 16 2.044000000000E+00 9.015000000000E-04 + 16 3.000000000000E+00 5.133000000000E-04 + 16 4.000000000000E+00 3.490000000000E-04 + 16 5.000000000000E+00 2.630000000000E-04 + 16 6.000000000000E+00 2.104000000000E-04 + 16 7.000000000000E+00 1.752000000000E-04 + 16 8.000000000000E+00 1.499000000000E-04 + 16 9.000000000000E+00 1.310000000000E-04 + 16 1.000000000000E+01 1.162000000000E-04 + 17 1.000000000000E-03 1.665000000000E+05 + 17 1.500000000000E-03 5.736000000000E+04 + 17 2.000000000000E-03 2.646000000000E+04 + 17 2.822000000000E-03 1.032000000000E+04 + 17 2.822000100000E-03 9.622000000000E+04 + 17 3.000000000000E-03 8.658000000000E+04 + 17 4.000000000000E-03 4.133000000000E+04 + 17 5.000000000000E-03 2.288000000000E+04 + 17 6.000000000000E-03 1.396000000000E+04 + 17 8.000000000000E-03 6.269000000000E+03 + 17 1.000000000000E-02 3.321000000000E+03 + 17 1.500000000000E-02 1.017000000000E+03 + 17 2.000000000000E-02 4.306000000000E+02 + 17 3.000000000000E-02 1.252000000000E+02 + 17 4.000000000000E-02 5.133000000000E+01 + 17 5.000000000000E-02 2.554000000000E+01 + 17 6.000000000000E-02 1.438000000000E+01 + 17 8.000000000000E-02 5.784000000000E+00 + 17 1.000000000000E-01 2.846000000000E+00 + 17 1.500000000000E-01 7.865000000000E-01 + 17 2.000000000000E-01 3.190000000000E-01 + 17 3.000000000000E-01 9.272000000000E-02 + 17 4.000000000000E-01 4.034000000000E-02 + 17 5.000000000000E-01 2.194000000000E-02 + 17 6.000000000000E-01 1.374000000000E-02 + 17 8.000000000000E-01 6.963000000000E-03 + 17 1.000000000000E+00 4.331000000000E-03 + 17 1.022000000000E+00 4.095000000000E-03 + 17 1.250000000000E+00 2.774000000000E-03 + 17 1.500000000000E+00 2.006000000000E-03 + 17 2.000000000000E+00 1.247000000000E-03 + 17 2.044000000000E+00 1.205000000000E-03 + 17 3.000000000000E+00 6.854000000000E-04 + 17 4.000000000000E+00 4.655000000000E-04 + 17 5.000000000000E+00 3.505000000000E-04 + 17 6.000000000000E+00 2.803000000000E-04 + 17 7.000000000000E+00 2.333000000000E-04 + 17 8.000000000000E+00 1.996000000000E-04 + 17 9.000000000000E+00 1.743000000000E-04 + 17 1.000000000000E+01 1.547000000000E-04 + 18 1.000000000000E-03 2.110000000000E+05 + 18 1.500000000000E-03 7.309000000000E+04 + 18 2.000000000000E-03 3.379000000000E+04 + 18 3.000000000000E-03 1.116000000000E+04 + 18 3.203000000000E-03 9.312000000000E+03 + 18 3.203000100000E-03 8.444000000000E+04 + 18 4.000000000000E-03 5.011000000000E+04 + 18 5.000000000000E-03 2.793000000000E+04 + 18 6.000000000000E-03 1.712000000000E+04 + 18 8.000000000000E-03 7.760000000000E+03 + 18 1.000000000000E-02 4.134000000000E+03 + 18 1.500000000000E-02 1.278000000000E+03 + 18 2.000000000000E-02 5.444000000000E+02 + 18 3.000000000000E-02 1.594000000000E+02 + 18 4.000000000000E-02 6.573000000000E+01 + 18 5.000000000000E-02 3.281000000000E+01 + 18 6.000000000000E-02 1.853000000000E+01 + 18 8.000000000000E-02 7.479000000000E+00 + 18 1.000000000000E-01 3.691000000000E+00 + 18 1.500000000000E-01 1.024000000000E+00 + 18 2.000000000000E-01 4.166000000000E-01 + 18 3.000000000000E-01 1.215000000000E-01 + 18 4.000000000000E-01 5.293000000000E-02 + 18 5.000000000000E-01 2.882000000000E-02 + 18 6.000000000000E-01 1.805000000000E-02 + 18 8.000000000000E-01 9.158000000000E-03 + 18 1.000000000000E+00 5.696000000000E-03 + 18 1.022000000000E+00 5.389000000000E-03 + 18 1.250000000000E+00 3.651000000000E-03 + 18 1.500000000000E+00 2.639000000000E-03 + 18 2.000000000000E+00 1.639000000000E-03 + 18 2.044000000000E+00 1.584000000000E-03 + 18 3.000000000000E+00 8.991000000000E-04 + 18 4.000000000000E+00 6.101000000000E-04 + 18 5.000000000000E+00 4.590000000000E-04 + 18 6.000000000000E+00 3.669000000000E-04 + 18 7.000000000000E+00 3.052000000000E-04 + 18 8.000000000000E+00 2.610000000000E-04 + 18 9.000000000000E+00 2.279000000000E-04 + 18 1.000000000000E+01 2.022000000000E-04 + 19 1.000000000000E-03 2.632000000000E+05 + 19 1.500000000000E-03 9.186000000000E+04 + 19 2.000000000000E-03 4.262000000000E+04 + 19 3.000000000000E-03 1.412000000000E+04 + 19 3.607000000000E-03 8.480000000000E+03 + 19 3.607000100000E-03 7.782000000000E+04 + 19 4.000000000000E-03 5.996000000000E+04 + 19 5.000000000000E-03 3.358000000000E+04 + 19 6.000000000000E-03 2.071000000000E+04 + 19 8.000000000000E-03 9.463000000000E+03 + 19 1.000000000000E-02 5.073000000000E+03 + 19 1.500000000000E-02 1.583000000000E+03 + 19 2.000000000000E-02 6.787000000000E+02 + 19 3.000000000000E-02 2.002000000000E+02 + 19 4.000000000000E-02 8.294000000000E+01 + 19 5.000000000000E-02 4.154000000000E+01 + 19 6.000000000000E-02 2.352000000000E+01 + 19 8.000000000000E-02 9.531000000000E+00 + 19 1.000000000000E-01 4.716000000000E+00 + 19 1.500000000000E-01 1.315000000000E+00 + 19 2.000000000000E-01 5.360000000000E-01 + 19 3.000000000000E-01 1.568000000000E-01 + 19 4.000000000000E-01 6.844000000000E-02 + 19 5.000000000000E-01 3.731000000000E-02 + 19 6.000000000000E-01 2.338000000000E-02 + 19 8.000000000000E-01 1.187000000000E-02 + 19 1.000000000000E+00 7.381000000000E-03 + 19 1.022000000000E+00 6.987000000000E-03 + 19 1.250000000000E+00 4.731000000000E-03 + 19 1.500000000000E+00 3.419000000000E-03 + 19 2.000000000000E+00 2.121000000000E-03 + 19 2.044000000000E+00 2.050000000000E-03 + 19 3.000000000000E+00 1.162000000000E-03 + 19 4.000000000000E+00 7.877000000000E-04 + 19 5.000000000000E+00 5.923000000000E-04 + 19 6.000000000000E+00 4.732000000000E-04 + 19 7.000000000000E+00 3.935000000000E-04 + 19 8.000000000000E+00 3.364000000000E-04 + 19 9.000000000000E+00 2.937000000000E-04 + 19 1.000000000000E+01 2.605000000000E-04 + 20 1.000000000000E-03 3.236000000000E+05 + 20 1.500000000000E-03 1.138000000000E+05 + 20 2.000000000000E-03 5.303000000000E+04 + 20 3.000000000000E-03 1.764000000000E+04 + 20 4.000000000000E-03 7.968000000000E+03 + 20 4.038000000000E-03 7.763000000000E+03 + 20 4.038000100000E-03 6.797000000000E+04 + 20 5.000000000000E-03 3.998000000000E+04 + 20 6.000000000000E-03 2.473000000000E+04 + 20 8.000000000000E-03 1.141000000000E+04 + 20 1.000000000000E-02 6.150000000000E+03 + 20 1.500000000000E-02 1.937000000000E+03 + 20 2.000000000000E-02 8.349000000000E+02 + 20 3.000000000000E-02 2.482000000000E+02 + 20 4.000000000000E-02 1.033000000000E+02 + 20 5.000000000000E-02 5.191000000000E+01 + 20 6.000000000000E-02 2.947000000000E+01 + 20 8.000000000000E-02 1.198000000000E+01 + 20 1.000000000000E-01 5.944000000000E+00 + 20 1.500000000000E-01 1.664000000000E+00 + 20 2.000000000000E-01 6.803000000000E-01 + 20 3.000000000000E-01 1.996000000000E-01 + 20 4.000000000000E-01 8.729000000000E-02 + 20 5.000000000000E-01 4.763000000000E-02 + 20 6.000000000000E-01 2.987000000000E-02 + 20 8.000000000000E-01 1.517000000000E-02 + 20 1.000000000000E+00 9.431000000000E-03 + 20 1.022000000000E+00 8.931000000000E-03 + 20 1.250000000000E+00 6.047000000000E-03 + 20 1.500000000000E+00 4.369000000000E-03 + 20 2.000000000000E+00 2.708000000000E-03 + 20 2.044000000000E+00 2.617000000000E-03 + 20 3.000000000000E+00 1.482000000000E-03 + 20 4.000000000000E+00 1.003000000000E-03 + 20 5.000000000000E+00 7.539000000000E-04 + 20 6.000000000000E+00 6.021000000000E-04 + 20 7.000000000000E+00 5.004000000000E-04 + 20 8.000000000000E+00 4.278000000000E-04 + 20 9.000000000000E+00 3.733000000000E-04 + 20 1.000000000000E+01 3.310000000000E-04 + 21 1.000000000000E-03 3.907000000000E+05 + 21 1.500000000000E-03 1.384000000000E+05 + 21 2.000000000000E-03 6.476000000000E+04 + 21 3.000000000000E-03 2.163000000000E+04 + 21 4.000000000000E-03 9.790000000000E+03 + 21 4.493000000000E-03 7.088000000000E+03 + 21 4.493000100000E-03 6.068000000000E+04 + 21 5.000000000000E-03 4.694000000000E+04 + 21 6.000000000000E-03 2.925000000000E+04 + 21 8.000000000000E-03 1.356000000000E+04 + 21 1.000000000000E-02 7.356000000000E+03 + 21 1.500000000000E-02 2.340000000000E+03 + 21 2.000000000000E-02 1.014000000000E+03 + 21 3.000000000000E-02 3.037000000000E+02 + 21 4.000000000000E-02 1.270000000000E+02 + 21 5.000000000000E-02 6.403000000000E+01 + 21 6.000000000000E-02 3.643000000000E+01 + 21 8.000000000000E-02 1.486000000000E+01 + 21 1.000000000000E-01 7.395000000000E+00 + 21 1.500000000000E-01 2.079000000000E+00 + 21 2.000000000000E-01 8.521000000000E-01 + 21 3.000000000000E-01 2.508000000000E-01 + 21 4.000000000000E-01 1.098000000000E-01 + 21 5.000000000000E-01 6.000000000000E-02 + 21 6.000000000000E-01 3.765000000000E-02 + 21 8.000000000000E-01 1.913000000000E-02 + 21 1.000000000000E+00 1.190000000000E-02 + 21 1.022000000000E+00 1.127000000000E-02 + 21 1.250000000000E+00 7.629000000000E-03 + 21 1.500000000000E+00 5.511000000000E-03 + 21 2.000000000000E+00 3.413000000000E-03 + 21 2.044000000000E+00 3.297000000000E-03 + 21 3.000000000000E+00 1.864000000000E-03 + 21 4.000000000000E+00 1.261000000000E-03 + 21 5.000000000000E+00 9.472000000000E-04 + 21 6.000000000000E+00 7.561000000000E-04 + 21 7.000000000000E+00 6.282000000000E-04 + 21 8.000000000000E+00 5.368000000000E-04 + 21 9.000000000000E+00 4.684000000000E-04 + 21 1.000000000000E+01 4.153000000000E-04 + 22 1.000000000000E-03 4.663000000000E+05 + 22 1.500000000000E-03 1.664000000000E+05 + 22 2.000000000000E-03 7.815000000000E+04 + 22 3.000000000000E-03 2.621000000000E+04 + 22 4.000000000000E-03 1.189000000000E+04 + 22 4.966000000000E-03 6.513000000000E+03 + 22 4.966000100000E-03 5.453000000000E+04 + 22 5.000000000000E-03 5.422000000000E+04 + 22 6.000000000000E-03 3.425000000000E+04 + 22 8.000000000000E-03 1.599000000000E+04 + 22 1.000000000000E-02 8.719000000000E+03 + 22 1.500000000000E-02 2.797000000000E+03 + 22 2.000000000000E-02 1.219000000000E+03 + 22 3.000000000000E-02 3.676000000000E+02 + 22 4.000000000000E-02 1.543000000000E+02 + 22 5.000000000000E-02 7.808000000000E+01 + 22 6.000000000000E-02 4.453000000000E+01 + 22 8.000000000000E-02 1.825000000000E+01 + 22 1.000000000000E-01 9.096000000000E+00 + 22 1.500000000000E-01 2.567000000000E+00 + 22 2.000000000000E-01 1.055000000000E+00 + 22 3.000000000000E-01 3.115000000000E-01 + 22 4.000000000000E-01 1.367000000000E-01 + 22 5.000000000000E-01 7.472000000000E-02 + 22 6.000000000000E-01 4.692000000000E-02 + 22 8.000000000000E-01 2.385000000000E-02 + 22 1.000000000000E+00 1.483000000000E-02 + 22 1.022000000000E+00 1.406000000000E-02 + 22 1.250000000000E+00 9.516000000000E-03 + 22 1.500000000000E+00 6.871000000000E-03 + 22 2.000000000000E+00 4.251000000000E-03 + 22 2.044000000000E+00 4.107000000000E-03 + 22 3.000000000000E+00 2.320000000000E-03 + 22 4.000000000000E+00 1.568000000000E-03 + 22 5.000000000000E+00 1.177000000000E-03 + 22 6.000000000000E+00 9.388000000000E-04 + 22 7.000000000000E+00 7.797000000000E-04 + 22 8.000000000000E+00 6.661000000000E-04 + 22 9.000000000000E+00 5.810000000000E-04 + 22 1.000000000000E+01 5.151000000000E-04 + 23 1.000000000000E-03 5.491000000000E+05 + 23 1.500000000000E-03 1.978000000000E+05 + 23 2.000000000000E-03 9.328000000000E+04 + 23 3.000000000000E-03 3.143000000000E+04 + 23 4.000000000000E-03 1.429000000000E+04 + 23 5.000000000000E-03 7.693000000000E+03 + 23 5.465000000000E-03 6.000000000000E+03 + 23 5.465000100000E-03 4.950000000000E+04 + 23 6.000000000000E-03 3.951000000000E+04 + 23 8.000000000000E-03 1.864000000000E+04 + 23 1.000000000000E-02 1.021000000000E+04 + 23 1.500000000000E-02 3.308000000000E+03 + 23 2.000000000000E-02 1.450000000000E+03 + 23 3.000000000000E-02 4.406000000000E+02 + 23 4.000000000000E-02 1.858000000000E+02 + 23 5.000000000000E-02 9.427000000000E+01 + 23 6.000000000000E-02 5.390000000000E+01 + 23 8.000000000000E-02 2.215000000000E+01 + 23 1.000000000000E-01 1.108000000000E+01 + 23 1.500000000000E-01 3.139000000000E+00 + 23 2.000000000000E-01 1.293000000000E+00 + 23 3.000000000000E-01 3.829000000000E-01 + 23 4.000000000000E-01 1.683000000000E-01 + 23 5.000000000000E-01 9.211000000000E-02 + 23 6.000000000000E-01 5.787000000000E-02 + 23 8.000000000000E-01 2.943000000000E-02 + 23 1.000000000000E+00 1.830000000000E-02 + 23 1.022000000000E+00 1.736000000000E-02 + 23 1.250000000000E+00 1.175000000000E-02 + 23 1.500000000000E+00 8.479000000000E-03 + 23 2.000000000000E+00 5.241000000000E-03 + 23 2.044000000000E+00 5.064000000000E-03 + 23 3.000000000000E+00 2.856000000000E-03 + 23 4.000000000000E+00 1.929000000000E-03 + 23 5.000000000000E+00 1.446000000000E-03 + 23 6.000000000000E+00 1.154000000000E-03 + 23 7.000000000000E+00 9.577000000000E-04 + 23 8.000000000000E+00 8.180000000000E-04 + 23 9.000000000000E+00 7.134000000000E-04 + 23 1.000000000000E+01 6.323000000000E-04 + 24 1.000000000000E-03 6.390000000000E+05 + 24 1.500000000000E-03 2.323000000000E+05 + 24 2.000000000000E-03 1.100000000000E+05 + 24 3.000000000000E-03 3.720000000000E+04 + 24 4.000000000000E-03 1.694000000000E+04 + 24 5.000000000000E-03 9.133000000000E+03 + 24 5.989000000000E-03 5.514000000000E+03 + 24 5.989000100000E-03 4.438000000000E+04 + 24 6.000000000000E-03 4.440000000000E+04 + 24 8.000000000000E-03 2.158000000000E+04 + 24 1.000000000000E-02 1.188000000000E+04 + 24 1.500000000000E-02 3.881000000000E+03 + 24 2.000000000000E-02 1.710000000000E+03 + 24 3.000000000000E-02 5.228000000000E+02 + 24 4.000000000000E-02 2.214000000000E+02 + 24 5.000000000000E-02 1.128000000000E+02 + 24 6.000000000000E-02 6.460000000000E+01 + 24 8.000000000000E-02 2.664000000000E+01 + 24 1.000000000000E-01 1.335000000000E+01 + 24 1.500000000000E-01 3.798000000000E+00 + 24 2.000000000000E-01 1.569000000000E+00 + 24 3.000000000000E-01 4.659000000000E-01 + 24 4.000000000000E-01 2.051000000000E-01 + 24 5.000000000000E-01 1.123000000000E-01 + 24 6.000000000000E-01 7.066000000000E-02 + 24 8.000000000000E-01 3.595000000000E-02 + 24 1.000000000000E+00 2.236000000000E-02 + 24 1.022000000000E+00 2.121000000000E-02 + 24 1.250000000000E+00 1.435000000000E-02 + 24 1.500000000000E+00 1.036000000000E-02 + 24 2.000000000000E+00 6.397000000000E-03 + 24 2.044000000000E+00 6.180000000000E-03 + 24 3.000000000000E+00 3.481000000000E-03 + 24 4.000000000000E+00 2.349000000000E-03 + 24 5.000000000000E+00 1.761000000000E-03 + 24 6.000000000000E+00 1.403000000000E-03 + 24 7.000000000000E+00 1.165000000000E-03 + 24 8.000000000000E+00 9.946000000000E-04 + 24 9.000000000000E+00 8.672000000000E-04 + 24 1.000000000000E+01 7.684000000000E-04 + 25 1.000000000000E-03 7.379000000000E+05 + 25 1.500000000000E-03 2.718000000000E+05 + 25 2.000000000000E-03 1.293000000000E+05 + 25 3.000000000000E-03 4.397000000000E+04 + 25 4.000000000000E-03 2.009000000000E+04 + 25 5.000000000000E-03 1.085000000000E+04 + 25 6.000000000000E-03 6.528000000000E+03 + 25 6.539000000000E-03 5.130000000000E+03 + 25 6.539000100000E-03 4.107000000000E+04 + 25 8.000000000000E-03 2.481000000000E+04 + 25 1.000000000000E-02 1.370000000000E+04 + 25 1.500000000000E-02 4.514000000000E+03 + 25 2.000000000000E-02 2.001000000000E+03 + 25 3.000000000000E-02 6.161000000000E+02 + 25 4.000000000000E-02 2.621000000000E+02 + 25 5.000000000000E-02 1.338000000000E+02 + 25 6.000000000000E-02 7.685000000000E+01 + 25 8.000000000000E-02 3.180000000000E+01 + 25 1.000000000000E-01 1.597000000000E+01 + 25 1.500000000000E-01 4.563000000000E+00 + 25 2.000000000000E-01 1.889000000000E+00 + 25 3.000000000000E-01 5.626000000000E-01 + 25 4.000000000000E-01 2.481000000000E-01 + 25 5.000000000000E-01 1.361000000000E-01 + 25 6.000000000000E-01 8.562000000000E-02 + 25 8.000000000000E-01 4.359000000000E-02 + 25 1.000000000000E+00 2.712000000000E-02 + 25 1.022000000000E+00 2.573000000000E-02 + 25 1.250000000000E+00 1.740000000000E-02 + 25 1.500000000000E+00 1.255000000000E-02 + 25 2.000000000000E+00 7.748000000000E-03 + 25 2.044000000000E+00 7.484000000000E-03 + 25 3.000000000000E+00 4.211000000000E-03 + 25 4.000000000000E+00 2.839000000000E-03 + 25 5.000000000000E+00 2.127000000000E-03 + 25 6.000000000000E+00 1.695000000000E-03 + 25 7.000000000000E+00 1.406000000000E-03 + 25 8.000000000000E+00 1.200000000000E-03 + 25 9.000000000000E+00 1.046000000000E-03 + 25 1.000000000000E+01 9.269000000000E-04 + 26 1.000000000000E-03 8.421000000000E+05 + 26 1.500000000000E-03 3.149000000000E+05 + 26 2.000000000000E-03 1.505000000000E+05 + 26 3.000000000000E-03 5.140000000000E+04 + 26 4.000000000000E-03 2.354000000000E+04 + 26 5.000000000000E-03 1.274000000000E+04 + 26 6.000000000000E-03 7.671000000000E+03 + 26 7.112000000000E-03 4.765000000000E+03 + 26 7.112000100000E-03 3.764000000000E+04 + 26 8.000000000000E-03 2.819000000000E+04 + 26 1.000000000000E-02 1.571000000000E+04 + 26 1.500000000000E-02 5.215000000000E+03 + 26 2.000000000000E-02 2.323000000000E+03 + 26 3.000000000000E-02 7.199000000000E+02 + 26 4.000000000000E-02 3.075000000000E+02 + 26 5.000000000000E-02 1.574000000000E+02 + 26 6.000000000000E-02 9.066000000000E+01 + 26 8.000000000000E-02 3.765000000000E+01 + 26 1.000000000000E-01 1.896000000000E+01 + 26 1.500000000000E-01 5.435000000000E+00 + 26 2.000000000000E-01 2.256000000000E+00 + 26 3.000000000000E-01 6.738000000000E-01 + 26 4.000000000000E-01 2.976000000000E-01 + 26 5.000000000000E-01 1.634000000000E-01 + 26 6.000000000000E-01 1.029000000000E-01 + 26 8.000000000000E-01 5.240000000000E-02 + 26 1.000000000000E+00 3.259000000000E-02 + 26 1.022000000000E+00 3.093000000000E-02 + 26 1.250000000000E+00 2.092000000000E-02 + 26 1.500000000000E+00 1.509000000000E-02 + 26 2.000000000000E+00 9.306000000000E-03 + 26 2.044000000000E+00 8.988000000000E-03 + 26 3.000000000000E+00 5.052000000000E-03 + 26 4.000000000000E+00 3.403000000000E-03 + 26 5.000000000000E+00 2.547000000000E-03 + 26 6.000000000000E+00 2.029000000000E-03 + 26 7.000000000000E+00 1.683000000000E-03 + 26 8.000000000000E+00 1.436000000000E-03 + 26 9.000000000000E+00 1.252000000000E-03 + 26 1.000000000000E+01 1.109000000000E-03 + 27 1.000000000000E-03 9.581000000000E+05 + 27 1.500000000000E-03 3.613000000000E+05 + 27 2.000000000000E-03 1.737000000000E+05 + 27 3.000000000000E-03 5.963000000000E+04 + 27 4.000000000000E-03 2.740000000000E+04 + 27 5.000000000000E-03 1.485000000000E+04 + 27 6.000000000000E-03 8.953000000000E+03 + 27 7.709000000000E-03 4.438000000000E+03 + 27 7.709000100000E-03 3.462000000000E+04 + 27 8.000000000000E-03 3.162000000000E+04 + 27 1.000000000000E-02 1.789000000000E+04 + 27 1.500000000000E-02 5.982000000000E+03 + 27 2.000000000000E-02 2.679000000000E+03 + 27 3.000000000000E-02 8.355000000000E+02 + 27 4.000000000000E-02 3.583000000000E+02 + 27 5.000000000000E-02 1.841000000000E+02 + 27 6.000000000000E-02 1.062000000000E+02 + 27 8.000000000000E-02 4.424000000000E+01 + 27 1.000000000000E-01 2.233000000000E+01 + 27 1.500000000000E-01 6.427000000000E+00 + 27 2.000000000000E-01 2.673000000000E+00 + 27 3.000000000000E-01 8.009000000000E-01 + 27 4.000000000000E-01 3.544000000000E-01 + 27 5.000000000000E-01 1.948000000000E-01 + 27 6.000000000000E-01 1.227000000000E-01 + 27 8.000000000000E-01 6.252000000000E-02 + 27 1.000000000000E+00 3.890000000000E-02 + 27 1.022000000000E+00 3.693000000000E-02 + 27 1.250000000000E+00 2.497000000000E-02 + 27 1.500000000000E+00 1.800000000000E-02 + 27 2.000000000000E+00 1.109000000000E-02 + 27 2.044000000000E+00 1.072000000000E-02 + 27 3.000000000000E+00 6.016000000000E-03 + 27 4.000000000000E+00 4.049000000000E-03 + 27 5.000000000000E+00 3.029000000000E-03 + 27 6.000000000000E+00 2.412000000000E-03 + 27 7.000000000000E+00 2.000000000000E-03 + 27 8.000000000000E+00 1.706000000000E-03 + 27 9.000000000000E+00 1.487000000000E-03 + 27 1.000000000000E+01 1.317000000000E-03 + 28 1.000000000000E-03 9.600000000000E+05 + 28 1.004000000000E-03 9.500000000000E+05 + 28 1.008000000000E-03 9.406000000000E+05 + 28 1.008000100000E-03 1.071000000000E+06 + 28 1.500000000000E-03 4.122000000000E+05 + 28 2.000000000000E-03 1.992000000000E+05 + 28 3.000000000000E-03 6.877000000000E+04 + 28 4.000000000000E-03 3.166000000000E+04 + 28 5.000000000000E-03 1.719000000000E+04 + 28 6.000000000000E-03 1.038000000000E+04 + 28 8.000000000000E-03 4.643000000000E+03 + 28 8.333000000000E-03 4.139000000000E+03 + 28 8.333000100000E-03 3.192000000000E+04 + 28 1.000000000000E-02 2.022000000000E+04 + 28 1.500000000000E-02 6.807000000000E+03 + 28 2.000000000000E-02 3.069000000000E+03 + 28 3.000000000000E-02 9.632000000000E+02 + 28 4.000000000000E-02 4.148000000000E+02 + 28 5.000000000000E-02 2.137000000000E+02 + 28 6.000000000000E-02 1.235000000000E+02 + 28 8.000000000000E-02 5.165000000000E+01 + 28 1.000000000000E-01 2.612000000000E+01 + 28 1.500000000000E-01 7.549000000000E+00 + 28 2.000000000000E-01 3.147000000000E+00 + 28 3.000000000000E-01 9.455000000000E-01 + 28 4.000000000000E-01 4.190000000000E-01 + 28 5.000000000000E-01 2.305000000000E-01 + 28 6.000000000000E-01 1.453000000000E-01 + 28 8.000000000000E-01 7.410000000000E-02 + 28 1.000000000000E+00 4.610000000000E-02 + 28 1.022000000000E+00 4.378000000000E-02 + 28 1.250000000000E+00 2.960000000000E-02 + 28 1.500000000000E+00 2.133000000000E-02 + 28 2.000000000000E+00 1.314000000000E-02 + 28 2.044000000000E+00 1.269000000000E-02 + 28 3.000000000000E+00 7.115000000000E-03 + 28 4.000000000000E+00 4.785000000000E-03 + 28 5.000000000000E+00 3.578000000000E-03 + 28 6.000000000000E+00 2.847000000000E-03 + 28 7.000000000000E+00 2.360000000000E-03 + 28 8.000000000000E+00 2.013000000000E-03 + 28 9.000000000000E+00 1.754000000000E-03 + 28 1.000000000000E+01 1.553000000000E-03 + 29 1.000000000000E-03 1.115000000000E+06 + 29 1.047000000000E-03 9.842000000000E+05 + 29 1.096000000000E-03 8.695000000000E+05 + 29 1.096000100000E-03 9.856000000000E+05 + 29 1.500000000000E-03 4.657000000000E+05 + 29 2.000000000000E-03 2.268000000000E+05 + 29 3.000000000000E-03 7.860000000000E+04 + 29 4.000000000000E-03 3.629000000000E+04 + 29 5.000000000000E-03 1.973000000000E+04 + 29 6.000000000000E-03 1.193000000000E+04 + 29 8.000000000000E-03 5.341000000000E+03 + 29 8.979000000000E-03 3.859000000000E+03 + 29 8.979000100000E-03 2.919000000000E+04 + 29 1.000000000000E-02 2.263000000000E+04 + 29 1.500000000000E-02 7.711000000000E+03 + 29 2.000000000000E-02 3.491000000000E+03 + 29 3.000000000000E-02 1.103000000000E+03 + 29 4.000000000000E-02 4.770000000000E+02 + 29 5.000000000000E-02 2.464000000000E+02 + 29 6.000000000000E-02 1.428000000000E+02 + 29 8.000000000000E-02 5.989000000000E+01 + 29 1.000000000000E-01 3.037000000000E+01 + 29 1.500000000000E-01 8.806000000000E+00 + 29 2.000000000000E-01 3.680000000000E+00 + 29 3.000000000000E-01 1.109000000000E+00 + 29 4.000000000000E-01 4.921000000000E-01 + 29 5.000000000000E-01 2.710000000000E-01 + 29 6.000000000000E-01 1.710000000000E-01 + 29 8.000000000000E-01 8.721000000000E-02 + 29 1.000000000000E+00 5.427000000000E-02 + 29 1.022000000000E+00 5.155000000000E-02 + 29 1.250000000000E+00 3.485000000000E-02 + 29 1.500000000000E+00 2.511000000000E-02 + 29 2.000000000000E+00 1.545000000000E-02 + 29 2.044000000000E+00 1.492000000000E-02 + 29 3.000000000000E+00 8.359000000000E-03 + 29 4.000000000000E+00 5.618000000000E-03 + 29 5.000000000000E+00 4.198000000000E-03 + 29 6.000000000000E+00 3.340000000000E-03 + 29 7.000000000000E+00 2.767000000000E-03 + 29 8.000000000000E+00 2.360000000000E-03 + 29 9.000000000000E+00 2.055000000000E-03 + 29 1.000000000000E+01 1.820000000000E-03 + 30 1.000000000000E-03 1.681000000000E+05 + 30 1.010000000000E-03 1.643000000000E+05 + 30 1.020000000000E-03 1.606000000000E+05 + 30 1.020000100000E-03 3.823000000000E+05 + 30 1.031000000000E-03 5.191000000000E+05 + 30 1.043000000000E-03 7.045000000000E+05 + 30 1.043000100000E-03 8.940000000000E+05 + 30 1.116000000000E-03 8.456000000000E+05 + 30 1.194000000000E-03 8.000000000000E+05 + 30 1.194000100000E-03 9.111000000000E+05 + 30 1.500000000000E-03 5.233000000000E+05 + 30 2.000000000000E-03 2.573000000000E+05 + 30 3.000000000000E-03 8.980000000000E+04 + 30 4.000000000000E-03 4.159000000000E+04 + 30 5.000000000000E-03 2.266000000000E+04 + 30 6.000000000000E-03 1.372000000000E+04 + 30 8.000000000000E-03 6.155000000000E+03 + 30 9.659000000000E-03 3.624000000000E+03 + 30 9.659000100000E-03 2.735000000000E+04 + 30 1.000000000000E-02 2.513000000000E+04 + 30 1.500000000000E-02 8.702000000000E+03 + 30 2.000000000000E-02 3.957000000000E+03 + 30 3.000000000000E-02 1.258000000000E+03 + 30 4.000000000000E-02 5.461000000000E+02 + 30 5.000000000000E-02 2.829000000000E+02 + 30 6.000000000000E-02 1.644000000000E+02 + 30 8.000000000000E-02 6.913000000000E+01 + 30 1.000000000000E-01 3.513000000000E+01 + 30 1.500000000000E-01 1.022000000000E+01 + 30 2.000000000000E-01 4.283000000000E+00 + 30 3.000000000000E-01 1.294000000000E+00 + 30 4.000000000000E-01 5.753000000000E-01 + 30 5.000000000000E-01 3.171000000000E-01 + 30 6.000000000000E-01 2.001000000000E-01 + 30 8.000000000000E-01 1.022000000000E-01 + 30 1.000000000000E+00 6.358000000000E-02 + 30 1.022000000000E+00 6.040000000000E-02 + 30 1.250000000000E+00 4.083000000000E-02 + 30 1.500000000000E+00 2.940000000000E-02 + 30 2.000000000000E+00 1.808000000000E-02 + 30 2.044000000000E+00 1.746000000000E-02 + 30 3.000000000000E+00 9.773000000000E-03 + 30 4.000000000000E+00 6.563000000000E-03 + 30 5.000000000000E+00 4.902000000000E-03 + 30 6.000000000000E+00 3.898000000000E-03 + 30 7.000000000000E+00 3.229000000000E-03 + 30 8.000000000000E+00 2.753000000000E-03 + 30 9.000000000000E+00 2.397000000000E-03 + 30 1.000000000000E+01 2.122000000000E-03 + 31 1.000000000000E-03 1.959000000000E+05 + 31 1.056000000000E-03 1.722000000000E+05 + 31 1.115000000000E-03 1.513000000000E+05 + 31 1.115000100000E-03 4.416000000000E+05 + 31 1.129000000000E-03 5.371000000000E+05 + 31 1.142000000000E-03 6.533000000000E+05 + 31 1.142000100000E-03 8.496000000000E+05 + 31 1.218000000000E-03 7.904000000000E+05 + 31 1.298000000000E-03 7.356000000000E+05 + 31 1.298000100000E-03 8.336000000000E+05 + 31 1.500000000000E-03 5.884000000000E+05 + 31 2.000000000000E-03 2.907000000000E+05 + 31 3.000000000000E-03 1.021000000000E+05 + 31 4.000000000000E-03 4.740000000000E+04 + 31 5.000000000000E-03 2.587000000000E+04 + 31 6.000000000000E-03 1.569000000000E+04 + 31 8.000000000000E-03 7.056000000000E+03 + 31 1.000000000000E-02 3.769000000000E+03 + 31 1.037000000000E-02 3.406000000000E+03 + 31 1.037000010000E-02 2.545000000000E+04 + 31 1.500000000000E-02 9.763000000000E+03 + 31 2.000000000000E-02 4.460000000000E+03 + 31 3.000000000000E-02 1.427000000000E+03 + 31 4.000000000000E-02 6.218000000000E+02 + 31 5.000000000000E-02 3.230000000000E+02 + 31 6.000000000000E-02 1.880000000000E+02 + 31 8.000000000000E-02 7.934000000000E+01 + 31 1.000000000000E-01 4.041000000000E+01 + 31 1.500000000000E-01 1.180000000000E+01 + 31 2.000000000000E-01 4.956000000000E+00 + 31 3.000000000000E-01 1.501000000000E+00 + 31 4.000000000000E-01 6.686000000000E-01 + 31 5.000000000000E-01 3.689000000000E-01 + 31 6.000000000000E-01 2.329000000000E-01 + 31 8.000000000000E-01 1.190000000000E-01 + 31 1.000000000000E+00 7.407000000000E-02 + 31 1.022000000000E+00 7.037000000000E-02 + 31 1.250000000000E+00 4.756000000000E-02 + 31 1.500000000000E+00 3.424000000000E-02 + 31 2.000000000000E+00 2.105000000000E-02 + 31 2.044000000000E+00 2.032000000000E-02 + 31 3.000000000000E+00 1.136000000000E-02 + 31 4.000000000000E+00 7.624000000000E-03 + 31 5.000000000000E+00 5.692000000000E-03 + 31 6.000000000000E+00 4.524000000000E-03 + 31 7.000000000000E+00 3.746000000000E-03 + 31 8.000000000000E+00 3.193000000000E-03 + 31 9.000000000000E+00 2.780000000000E-03 + 31 1.000000000000E+01 2.460000000000E-03 + 32 1.000000000000E-03 2.275000000000E+05 + 32 1.103000000000E-03 1.803000000000E+05 + 32 1.217000000000E-03 1.429000000000E+05 + 32 1.217000100000E-03 5.252000000000E+05 + 32 1.232000000000E-03 5.607000000000E+05 + 32 1.248000000000E-03 5.988000000000E+05 + 32 1.248000100000E-03 8.018000000000E+05 + 32 1.328000000000E-03 7.323000000000E+05 + 32 1.414000000000E-03 6.690000000000E+05 + 32 1.414000100000E-03 7.574000000000E+05 + 32 1.500000000000E-03 6.595000000000E+05 + 32 2.000000000000E-03 3.263000000000E+05 + 32 3.000000000000E-03 1.154000000000E+05 + 32 4.000000000000E-03 5.379000000000E+04 + 32 5.000000000000E-03 2.943000000000E+04 + 32 6.000000000000E-03 1.786000000000E+04 + 32 8.000000000000E-03 8.048000000000E+03 + 32 1.000000000000E-02 4.306000000000E+03 + 32 1.110000000000E-02 3.206000000000E+03 + 32 1.110000010000E-02 2.371000000000E+04 + 32 1.500000000000E-02 1.090000000000E+04 + 32 2.000000000000E-02 4.996000000000E+03 + 32 3.000000000000E-02 1.610000000000E+03 + 32 4.000000000000E-02 7.045000000000E+02 + 32 5.000000000000E-02 3.670000000000E+02 + 32 6.000000000000E-02 2.140000000000E+02 + 32 8.000000000000E-02 9.061000000000E+01 + 32 1.000000000000E-01 4.624000000000E+01 + 32 1.500000000000E-01 1.356000000000E+01 + 32 2.000000000000E-01 5.706000000000E+00 + 32 3.000000000000E-01 1.733000000000E+00 + 32 4.000000000000E-01 7.731000000000E-01 + 32 5.000000000000E-01 4.270000000000E-01 + 32 6.000000000000E-01 2.699000000000E-01 + 32 8.000000000000E-01 1.379000000000E-01 + 32 1.000000000000E+00 8.585000000000E-02 + 32 1.022000000000E+00 8.158000000000E-02 + 32 1.250000000000E+00 5.512000000000E-02 + 32 1.500000000000E+00 3.968000000000E-02 + 32 2.000000000000E+00 2.437000000000E-02 + 32 2.044000000000E+00 2.353000000000E-02 + 32 3.000000000000E+00 1.314000000000E-02 + 32 4.000000000000E+00 8.813000000000E-03 + 32 5.000000000000E+00 6.576000000000E-03 + 32 6.000000000000E+00 5.225000000000E-03 + 32 7.000000000000E+00 4.325000000000E-03 + 32 8.000000000000E+00 3.685000000000E-03 + 32 9.000000000000E+00 3.208000000000E-03 + 32 1.000000000000E+01 2.839000000000E-03 + 33 1.000000000000E-03 2.632000000000E+05 + 33 1.150000000000E-03 1.887000000000E+05 + 33 1.323000000000E-03 1.352000000000E+05 + 33 1.323000100000E-03 5.632000000000E+05 + 33 1.341000000000E-03 5.579000000000E+05 + 33 1.359000000000E-03 5.525000000000E+05 + 33 1.359000100000E-03 7.561000000000E+05 + 33 1.500000000000E-03 6.495000000000E+05 + 33 1.526000000000E-03 6.211000000000E+05 + 33 1.526000100000E-03 7.027000000000E+05 + 33 2.000000000000E-03 3.641000000000E+05 + 33 3.000000000000E-03 1.300000000000E+05 + 33 4.000000000000E-03 6.076000000000E+04 + 33 5.000000000000E-03 3.330000000000E+04 + 33 6.000000000000E-03 2.025000000000E+04 + 33 8.000000000000E-03 9.143000000000E+03 + 33 1.000000000000E-02 4.898000000000E+03 + 33 1.187000000000E-02 3.023000000000E+03 + 33 1.187000010000E-02 2.212000000000E+04 + 33 1.500000000000E-02 1.212000000000E+04 + 33 2.000000000000E-02 5.578000000000E+03 + 33 3.000000000000E-02 1.809000000000E+03 + 33 4.000000000000E-02 7.945000000000E+02 + 33 5.000000000000E-02 4.150000000000E+02 + 33 6.000000000000E-02 2.426000000000E+02 + 33 8.000000000000E-02 1.030000000000E+02 + 33 1.000000000000E-01 5.268000000000E+01 + 33 1.500000000000E-01 1.551000000000E+01 + 33 2.000000000000E-01 6.537000000000E+00 + 33 3.000000000000E-01 1.991000000000E+00 + 33 4.000000000000E-01 8.897000000000E-01 + 33 5.000000000000E-01 4.919000000000E-01 + 33 6.000000000000E-01 3.111000000000E-01 + 33 8.000000000000E-01 1.591000000000E-01 + 33 1.000000000000E+00 9.902000000000E-02 + 33 1.022000000000E+00 9.411000000000E-02 + 33 1.250000000000E+00 6.358000000000E-02 + 33 1.500000000000E+00 4.575000000000E-02 + 33 2.000000000000E+00 2.808000000000E-02 + 33 2.044000000000E+00 2.712000000000E-02 + 33 3.000000000000E+00 1.513000000000E-02 + 33 4.000000000000E+00 1.014000000000E-02 + 33 5.000000000000E+00 7.561000000000E-03 + 33 6.000000000000E+00 6.005000000000E-03 + 33 7.000000000000E+00 4.970000000000E-03 + 33 8.000000000000E+00 4.234000000000E-03 + 33 9.000000000000E+00 3.685000000000E-03 + 33 1.000000000000E+01 3.260000000000E-03 + 34 1.000000000000E-03 3.031000000000E+05 + 34 1.198000000000E-03 1.969000000000E+05 + 34 1.436000000000E-03 1.280000000000E+05 + 34 1.436000100000E-03 5.695000000000E+05 + 34 1.456000000000E-03 5.398000000000E+05 + 34 1.476000000000E-03 5.116000000000E+05 + 34 1.476000100000E-03 7.328000000000E+05 + 34 1.500000000000E-03 6.989000000000E+05 + 34 1.654000000000E-03 5.687000000000E+05 + 34 1.654000100000E-03 6.438000000000E+05 + 34 2.000000000000E-03 4.056000000000E+05 + 34 3.000000000000E-03 1.458000000000E+05 + 34 4.000000000000E-03 6.837000000000E+04 + 34 5.000000000000E-03 3.755000000000E+04 + 34 6.000000000000E-03 2.287000000000E+04 + 34 8.000000000000E-03 1.034000000000E+04 + 34 1.000000000000E-02 5.549000000000E+03 + 34 1.266000000000E-02 2.857000000000E+03 + 34 1.266000010000E-02 2.065000000000E+04 + 34 1.500000000000E-02 1.339000000000E+04 + 34 2.000000000000E-02 6.210000000000E+03 + 34 3.000000000000E-02 2.025000000000E+03 + 34 4.000000000000E-02 8.924000000000E+02 + 34 5.000000000000E-02 4.673000000000E+02 + 34 6.000000000000E-02 2.737000000000E+02 + 34 8.000000000000E-02 1.166000000000E+02 + 34 1.000000000000E-01 5.976000000000E+01 + 34 1.500000000000E-01 1.764000000000E+01 + 34 2.000000000000E-01 7.458000000000E+00 + 34 3.000000000000E-01 2.278000000000E+00 + 34 4.000000000000E-01 1.019000000000E+00 + 34 5.000000000000E-01 5.641000000000E-01 + 34 6.000000000000E-01 3.568000000000E-01 + 34 8.000000000000E-01 1.826000000000E-01 + 34 1.000000000000E+00 1.137000000000E-01 + 34 1.022000000000E+00 1.081000000000E-01 + 34 1.250000000000E+00 7.300000000000E-02 + 34 1.500000000000E+00 5.252000000000E-02 + 34 2.000000000000E+00 3.222000000000E-02 + 34 2.044000000000E+00 3.111000000000E-02 + 34 3.000000000000E+00 1.734000000000E-02 + 34 4.000000000000E+00 1.161000000000E-02 + 34 5.000000000000E+00 8.656000000000E-03 + 34 6.000000000000E+00 6.872000000000E-03 + 34 7.000000000000E+00 5.686000000000E-03 + 34 8.000000000000E+00 4.843000000000E-03 + 34 9.000000000000E+00 4.214000000000E-03 + 34 1.000000000000E+01 3.728000000000E-03 + 35 1.000000000000E-03 3.474000000000E+05 + 35 1.500000000000E-03 1.322000000000E+05 + 35 1.550000000000E-03 1.221000000000E+05 + 35 1.550000100000E-03 5.714000000000E+05 + 35 1.573000000000E-03 5.216000000000E+05 + 35 1.596000000000E-03 4.760000000000E+05 + 35 1.596000100000E-03 6.763000000000E+05 + 35 1.686000000000E-03 5.964000000000E+05 + 35 1.782000000000E-03 5.260000000000E+05 + 35 1.782000100000E-03 5.956000000000E+05 + 35 2.000000000000E-03 4.514000000000E+05 + 35 3.000000000000E-03 1.627000000000E+05 + 35 4.000000000000E-03 7.664000000000E+04 + 35 5.000000000000E-03 4.218000000000E+04 + 35 6.000000000000E-03 2.572000000000E+04 + 35 8.000000000000E-03 1.166000000000E+04 + 35 1.000000000000E-02 6.263000000000E+03 + 35 1.347000000000E-02 2.706000000000E+03 + 35 1.347000010000E-02 1.933000000000E+04 + 35 1.500000000000E-02 1.468000000000E+04 + 35 2.000000000000E-02 6.872000000000E+03 + 35 3.000000000000E-02 2.254000000000E+03 + 35 4.000000000000E-02 9.957000000000E+02 + 35 5.000000000000E-02 5.241000000000E+02 + 35 6.000000000000E-02 3.076000000000E+02 + 35 8.000000000000E-02 1.314000000000E+02 + 35 1.000000000000E-01 6.749000000000E+01 + 35 1.500000000000E-01 2.000000000000E+01 + 35 2.000000000000E-01 8.472000000000E+00 + 35 3.000000000000E-01 2.595000000000E+00 + 35 4.000000000000E-01 1.163000000000E+00 + 35 5.000000000000E-01 6.441000000000E-01 + 35 6.000000000000E-01 4.078000000000E-01 + 35 8.000000000000E-01 2.088000000000E-01 + 35 1.000000000000E+00 1.300000000000E-01 + 35 1.022000000000E+00 1.236000000000E-01 + 35 1.250000000000E+00 8.348000000000E-02 + 35 1.500000000000E+00 6.003000000000E-02 + 35 2.000000000000E+00 3.681000000000E-02 + 35 2.044000000000E+00 3.554000000000E-02 + 35 3.000000000000E+00 1.979000000000E-02 + 35 4.000000000000E+00 1.325000000000E-02 + 35 5.000000000000E+00 9.868000000000E-03 + 35 6.000000000000E+00 7.832000000000E-03 + 35 7.000000000000E+00 6.478000000000E-03 + 35 8.000000000000E+00 5.516000000000E-03 + 35 9.000000000000E+00 4.799000000000E-03 + 35 1.000000000000E+01 4.245000000000E-03 + 36 1.000000000000E-03 3.963000000000E+05 + 36 1.500000000000E-03 1.513000000000E+05 + 36 1.675000000000E-03 1.156000000000E+05 + 36 1.675000100000E-03 5.442000000000E+05 + 36 1.701000000000E-03 4.890000000000E+05 + 36 1.727000000000E-03 4.397000000000E+05 + 36 1.727000100000E-03 6.344000000000E+05 + 36 1.822000000000E-03 5.540000000000E+05 + 36 1.921000000000E-03 4.840000000000E+05 + 36 1.921000100000E-03 5.486000000000E+05 + 36 2.000000000000E-03 5.002000000000E+05 + 36 3.000000000000E-03 1.810000000000E+05 + 36 4.000000000000E-03 8.555000000000E+04 + 36 5.000000000000E-03 4.719000000000E+04 + 36 6.000000000000E-03 2.882000000000E+04 + 36 8.000000000000E-03 1.309000000000E+04 + 36 1.000000000000E-02 7.044000000000E+03 + 36 1.433000000000E-02 2.562000000000E+03 + 36 1.433000010000E-02 1.809000000000E+04 + 36 1.500000000000E-02 1.608000000000E+04 + 36 2.000000000000E-02 7.598000000000E+03 + 36 3.000000000000E-02 2.502000000000E+03 + 36 4.000000000000E-02 1.112000000000E+03 + 36 5.000000000000E-02 5.854000000000E+02 + 36 6.000000000000E-02 3.443000000000E+02 + 36 8.000000000000E-02 1.475000000000E+02 + 36 1.000000000000E-01 7.593000000000E+01 + 36 1.500000000000E-01 2.258000000000E+01 + 36 2.000000000000E-01 9.585000000000E+00 + 36 3.000000000000E-01 2.944000000000E+00 + 36 4.000000000000E-01 1.321000000000E+00 + 36 5.000000000000E-01 7.327000000000E-01 + 36 6.000000000000E-01 4.641000000000E-01 + 36 8.000000000000E-01 2.378000000000E-01 + 36 1.000000000000E+00 1.480000000000E-01 + 36 1.022000000000E+00 1.408000000000E-01 + 36 1.250000000000E+00 9.508000000000E-02 + 36 1.500000000000E+00 6.836000000000E-02 + 36 2.000000000000E+00 4.189000000000E-02 + 36 2.044000000000E+00 4.044000000000E-02 + 36 3.000000000000E+00 2.251000000000E-02 + 36 4.000000000000E+00 1.505000000000E-02 + 36 5.000000000000E+00 1.121000000000E-02 + 36 6.000000000000E+00 8.891000000000E-03 + 36 7.000000000000E+00 7.352000000000E-03 + 36 8.000000000000E+00 6.259000000000E-03 + 36 9.000000000000E+00 5.444000000000E-03 + 36 1.000000000000E+01 4.815000000000E-03 + 37 1.000000000000E-03 4.496000000000E+05 + 37 1.500000000000E-03 1.722000000000E+05 + 37 1.804000000000E-03 1.097000000000E+05 + 37 1.804000100000E-03 4.386000000000E+05 + 37 1.834000000000E-03 4.216000000000E+05 + 37 1.864000000000E-03 4.053000000000E+05 + 37 1.864000100000E-03 5.609000000000E+05 + 37 2.000000000000E-03 4.832000000000E+05 + 37 2.065000000000E-03 4.468000000000E+05 + 37 2.065000100000E-03 5.111000000000E+05 + 37 3.000000000000E-03 2.006000000000E+05 + 37 4.000000000000E-03 9.520000000000E+04 + 37 5.000000000000E-03 5.264000000000E+04 + 37 6.000000000000E-03 3.220000000000E+04 + 37 8.000000000000E-03 1.466000000000E+04 + 37 1.000000000000E-02 7.897000000000E+03 + 37 1.500000000000E-02 2.527000000000E+03 + 37 1.520000000000E-02 2.434000000000E+03 + 37 1.520000010000E-02 1.697000000000E+04 + 37 2.000000000000E-02 8.357000000000E+03 + 37 3.000000000000E-02 2.769000000000E+03 + 37 4.000000000000E-02 1.234000000000E+03 + 37 5.000000000000E-02 6.517000000000E+02 + 37 6.000000000000E-02 3.840000000000E+02 + 37 8.000000000000E-02 1.650000000000E+02 + 37 1.000000000000E-01 8.512000000000E+01 + 37 1.500000000000E-01 2.540000000000E+01 + 37 2.000000000000E-01 1.081000000000E+01 + 37 3.000000000000E-01 3.327000000000E+00 + 37 4.000000000000E-01 1.496000000000E+00 + 37 5.000000000000E-01 8.302000000000E-01 + 37 6.000000000000E-01 5.262000000000E-01 + 37 8.000000000000E-01 2.698000000000E-01 + 37 1.000000000000E+00 1.680000000000E-01 + 37 1.022000000000E+00 1.598000000000E-01 + 37 1.250000000000E+00 1.079000000000E-01 + 37 1.500000000000E+00 7.755000000000E-02 + 37 2.000000000000E+00 4.750000000000E-02 + 37 2.044000000000E+00 4.586000000000E-02 + 37 3.000000000000E+00 2.550000000000E-02 + 37 4.000000000000E+00 1.704000000000E-02 + 37 5.000000000000E+00 1.268000000000E-02 + 37 6.000000000000E+00 1.006000000000E-02 + 37 7.000000000000E+00 8.314000000000E-03 + 37 8.000000000000E+00 7.077000000000E-03 + 37 9.000000000000E+00 6.155000000000E-03 + 37 1.000000000000E+01 5.442000000000E-03 + 38 1.000000000000E-03 5.074000000000E+05 + 38 1.500000000000E-03 1.951000000000E+05 + 38 1.940000000000E-03 1.041000000000E+05 + 38 1.940000100000E-03 4.158000000000E+05 + 38 2.000000000000E-03 3.760000000000E+05 + 38 2.007000000000E-03 3.734000000000E+05 + 38 2.007000100000E-03 5.198000000000E+05 + 38 2.109000000000E-03 4.633000000000E+05 + 38 2.216000000000E-03 4.128000000000E+05 + 38 2.216000100000E-03 4.708000000000E+05 + 38 3.000000000000E-03 2.213000000000E+05 + 38 4.000000000000E-03 1.056000000000E+05 + 38 5.000000000000E-03 5.852000000000E+04 + 38 6.000000000000E-03 3.585000000000E+04 + 38 8.000000000000E-03 1.636000000000E+04 + 38 1.000000000000E-02 8.825000000000E+03 + 38 1.500000000000E-02 2.831000000000E+03 + 38 1.610000000000E-02 2.315000000000E+03 + 38 1.610000010000E-02 1.595000000000E+04 + 38 2.000000000000E-02 9.154000000000E+03 + 38 3.000000000000E-02 3.051000000000E+03 + 38 4.000000000000E-02 1.366000000000E+03 + 38 5.000000000000E-02 7.231000000000E+02 + 38 6.000000000000E-02 4.270000000000E+02 + 38 8.000000000000E-02 1.840000000000E+02 + 38 1.000000000000E-01 9.510000000000E+01 + 38 1.500000000000E-01 2.847000000000E+01 + 38 2.000000000000E-01 1.214000000000E+01 + 38 3.000000000000E-01 3.748000000000E+00 + 38 4.000000000000E-01 1.688000000000E+00 + 38 5.000000000000E-01 9.377000000000E-01 + 38 6.000000000000E-01 5.947000000000E-01 + 38 8.000000000000E-01 3.051000000000E-01 + 38 1.000000000000E+00 1.901000000000E-01 + 38 1.022000000000E+00 1.807000000000E-01 + 38 1.250000000000E+00 1.220000000000E-01 + 38 1.500000000000E+00 8.768000000000E-02 + 38 2.000000000000E+00 5.368000000000E-02 + 38 2.044000000000E+00 5.181000000000E-02 + 38 3.000000000000E+00 2.879000000000E-02 + 38 4.000000000000E+00 1.922000000000E-02 + 38 5.000000000000E+00 1.430000000000E-02 + 38 6.000000000000E+00 1.134000000000E-02 + 38 7.000000000000E+00 9.370000000000E-03 + 38 8.000000000000E+00 7.974000000000E-03 + 38 9.000000000000E+00 6.934000000000E-03 + 38 1.000000000000E+01 6.130000000000E-03 + 39 1.000000000000E-03 5.695000000000E+05 + 39 1.500000000000E-03 2.196000000000E+05 + 39 2.000000000000E-03 1.087000000000E+05 + 39 2.080000000000E-03 9.867000000000E+04 + 39 2.080000100000E-03 3.868000000000E+05 + 39 2.117000000000E-03 3.653000000000E+05 + 39 2.155000000000E-03 3.449000000000E+05 + 39 2.155000100000E-03 4.809000000000E+05 + 39 2.261000000000E-03 4.289000000000E+05 + 39 2.373000000000E-03 3.828000000000E+05 + 39 2.373000100000E-03 4.364000000000E+05 + 39 3.000000000000E-03 2.435000000000E+05 + 39 4.000000000000E-03 1.165000000000E+05 + 39 5.000000000000E-03 6.477000000000E+04 + 39 6.000000000000E-03 3.975000000000E+04 + 39 8.000000000000E-03 1.818000000000E+04 + 39 1.000000000000E-02 9.825000000000E+03 + 39 1.500000000000E-02 3.158000000000E+03 + 39 1.704000000000E-02 2.202000000000E+03 + 39 1.704000010000E-02 1.501000000000E+04 + 39 2.000000000000E-02 9.975000000000E+03 + 39 3.000000000000E-02 3.350000000000E+03 + 39 4.000000000000E-02 1.506000000000E+03 + 39 5.000000000000E-02 7.996000000000E+02 + 39 6.000000000000E-02 4.730000000000E+02 + 39 8.000000000000E-02 2.044000000000E+02 + 39 1.000000000000E-01 1.059000000000E+02 + 39 1.500000000000E-01 3.181000000000E+01 + 39 2.000000000000E-01 1.359000000000E+01 + 39 3.000000000000E-01 4.206000000000E+00 + 39 4.000000000000E-01 1.897000000000E+00 + 39 5.000000000000E-01 1.055000000000E+00 + 39 6.000000000000E-01 6.697000000000E-01 + 39 8.000000000000E-01 3.437000000000E-01 + 39 1.000000000000E+00 2.142000000000E-01 + 39 1.022000000000E+00 2.037000000000E-01 + 39 1.250000000000E+00 1.375000000000E-01 + 39 1.500000000000E+00 9.878000000000E-02 + 39 2.000000000000E+00 6.044000000000E-02 + 39 2.044000000000E+00 5.834000000000E-02 + 39 3.000000000000E+00 3.239000000000E-02 + 39 4.000000000000E+00 2.162000000000E-02 + 39 5.000000000000E+00 1.607000000000E-02 + 39 6.000000000000E+00 1.274000000000E-02 + 39 7.000000000000E+00 1.052000000000E-02 + 39 8.000000000000E+00 8.954000000000E-03 + 39 9.000000000000E+00 7.785000000000E-03 + 39 1.000000000000E+01 6.882000000000E-03 + 40 1.000000000000E-03 6.367000000000E+05 + 40 1.500000000000E-03 2.462000000000E+05 + 40 2.000000000000E-03 1.221000000000E+05 + 40 2.222000000000E-03 9.396000000000E+04 + 40 2.222000100000E-03 3.614000000000E+05 + 40 2.264000000000E-03 3.402000000000E+05 + 40 2.307000000000E-03 3.203000000000E+05 + 40 2.307000100000E-03 4.465000000000E+05 + 40 2.417000000000E-03 3.989000000000E+05 + 40 2.532000000000E-03 3.565000000000E+05 + 40 2.532000100000E-03 4.068000000000E+05 + 40 3.000000000000E-03 2.677000000000E+05 + 40 4.000000000000E-03 1.282000000000E+05 + 40 5.000000000000E-03 7.147000000000E+04 + 40 6.000000000000E-03 4.395000000000E+04 + 40 8.000000000000E-03 2.014000000000E+04 + 40 1.000000000000E-02 1.090000000000E+04 + 40 1.500000000000E-02 3.512000000000E+03 + 40 1.800000000000E-02 2.099000000000E+03 + 40 1.800000010000E-02 1.417000000000E+04 + 40 2.000000000000E-02 1.081000000000E+04 + 40 3.000000000000E-02 3.669000000000E+03 + 40 4.000000000000E-02 1.656000000000E+03 + 40 5.000000000000E-02 8.813000000000E+02 + 40 6.000000000000E-02 5.224000000000E+02 + 40 8.000000000000E-02 2.264000000000E+02 + 40 1.000000000000E-01 1.175000000000E+02 + 40 1.500000000000E-01 3.542000000000E+01 + 40 2.000000000000E-01 1.517000000000E+01 + 40 3.000000000000E-01 4.707000000000E+00 + 40 4.000000000000E-01 2.126000000000E+00 + 40 5.000000000000E-01 1.184000000000E+00 + 40 6.000000000000E-01 7.517000000000E-01 + 40 8.000000000000E-01 3.861000000000E-01 + 40 1.000000000000E+00 2.406000000000E-01 + 40 1.022000000000E+00 2.288000000000E-01 + 40 1.250000000000E+00 1.544000000000E-01 + 40 1.500000000000E+00 1.109000000000E-01 + 40 2.000000000000E+00 6.785000000000E-02 + 40 2.044000000000E+00 6.549000000000E-02 + 40 3.000000000000E+00 3.632000000000E-02 + 40 4.000000000000E+00 2.423000000000E-02 + 40 5.000000000000E+00 1.801000000000E-02 + 40 6.000000000000E+00 1.427000000000E-02 + 40 7.000000000000E+00 1.178000000000E-02 + 40 8.000000000000E+00 1.002000000000E-02 + 40 9.000000000000E+00 8.713000000000E-03 + 40 1.000000000000E+01 7.701000000000E-03 + 41 1.000000000000E-03 7.085000000000E+05 + 41 1.500000000000E-03 2.746000000000E+05 + 41 2.000000000000E-03 1.363000000000E+05 + 41 2.370000000000E-03 8.929000000000E+04 + 41 2.370000100000E-03 3.356000000000E+05 + 41 2.417000000000E-03 3.160000000000E+05 + 41 2.465000000000E-03 2.977000000000E+05 + 41 2.465000100000E-03 4.147000000000E+05 + 41 2.579000000000E-03 3.714000000000E+05 + 41 2.698000000000E-03 3.326000000000E+05 + 41 2.698000100000E-03 3.802000000000E+05 + 41 3.000000000000E-03 2.932000000000E+05 + 41 4.000000000000E-03 1.407000000000E+05 + 41 5.000000000000E-03 7.859000000000E+04 + 41 6.000000000000E-03 4.840000000000E+04 + 41 8.000000000000E-03 2.223000000000E+04 + 41 1.000000000000E-02 1.205000000000E+04 + 41 1.500000000000E-02 3.891000000000E+03 + 41 1.899000000000E-02 2.000000000000E+03 + 41 1.899000010000E-02 1.337000000000E+04 + 41 2.000000000000E-02 1.173000000000E+04 + 41 3.000000000000E-02 4.011000000000E+03 + 41 4.000000000000E-02 1.815000000000E+03 + 41 5.000000000000E-02 9.683000000000E+02 + 41 6.000000000000E-02 5.753000000000E+02 + 41 8.000000000000E-02 2.500000000000E+02 + 41 1.000000000000E-01 1.300000000000E+02 + 41 1.500000000000E-01 3.932000000000E+01 + 41 2.000000000000E-01 1.687000000000E+01 + 41 3.000000000000E-01 5.249000000000E+00 + 41 4.000000000000E-01 2.375000000000E+00 + 41 5.000000000000E-01 1.324000000000E+00 + 41 6.000000000000E-01 8.411000000000E-01 + 41 8.000000000000E-01 4.323000000000E-01 + 41 1.000000000000E+00 2.694000000000E-01 + 41 1.022000000000E+00 2.563000000000E-01 + 41 1.250000000000E+00 1.730000000000E-01 + 41 1.500000000000E+00 1.242000000000E-01 + 41 2.000000000000E+00 7.592000000000E-02 + 41 2.044000000000E+00 7.327000000000E-02 + 41 3.000000000000E+00 4.061000000000E-02 + 41 4.000000000000E+00 2.707000000000E-02 + 41 5.000000000000E+00 2.011000000000E-02 + 41 6.000000000000E+00 1.593000000000E-02 + 41 7.000000000000E+00 1.315000000000E-02 + 41 8.000000000000E+00 1.119000000000E-02 + 41 9.000000000000E+00 9.721000000000E-03 + 41 1.000000000000E+01 8.591000000000E-03 + 42 1.000000000000E-03 7.863000000000E+05 + 42 1.500000000000E-03 3.056000000000E+05 + 42 2.000000000000E-03 1.519000000000E+05 + 42 2.520000000000E-03 8.537000000000E+04 + 42 2.520000100000E-03 3.143000000000E+05 + 42 2.572000000000E-03 2.955000000000E+05 + 42 2.625000000000E-03 2.779000000000E+05 + 42 2.625000100000E-03 3.867000000000E+05 + 42 2.743000000000E-03 3.471000000000E+05 + 42 2.865000000000E-03 3.116000000000E+05 + 42 2.865000100000E-03 3.564000000000E+05 + 42 3.000000000000E-03 3.196000000000E+05 + 42 4.000000000000E-03 1.539000000000E+05 + 42 5.000000000000E-03 8.619000000000E+04 + 42 6.000000000000E-03 5.315000000000E+04 + 42 8.000000000000E-03 2.449000000000E+04 + 42 1.000000000000E-02 1.329000000000E+04 + 42 1.500000000000E-02 4.302000000000E+03 + 42 2.000000000000E-02 1.911000000000E+03 + 42 2.000000010000E-02 1.250000000000E+04 + 42 2.000000000000E-02 1.250000000000E+04 + 42 3.000000000000E-02 4.370000000000E+03 + 42 4.000000000000E-02 1.985000000000E+03 + 42 5.000000000000E-02 1.062000000000E+03 + 42 6.000000000000E-02 6.318000000000E+02 + 42 8.000000000000E-02 2.753000000000E+02 + 42 1.000000000000E-01 1.435000000000E+02 + 42 1.500000000000E-01 4.353000000000E+01 + 42 2.000000000000E-01 1.872000000000E+01 + 42 3.000000000000E-01 5.839000000000E+00 + 42 4.000000000000E-01 2.646000000000E+00 + 42 5.000000000000E-01 1.476000000000E+00 + 42 6.000000000000E-01 9.386000000000E-01 + 42 8.000000000000E-01 4.827000000000E-01 + 42 1.000000000000E+00 3.008000000000E-01 + 42 1.022000000000E+00 2.862000000000E-01 + 42 1.250000000000E+00 1.932000000000E-01 + 42 1.500000000000E+00 1.386000000000E-01 + 42 2.000000000000E+00 8.472000000000E-02 + 42 2.044000000000E+00 8.176000000000E-02 + 42 3.000000000000E+00 4.528000000000E-02 + 42 4.000000000000E+00 3.017000000000E-02 + 42 5.000000000000E+00 2.240000000000E-02 + 42 6.000000000000E+00 1.774000000000E-02 + 42 7.000000000000E+00 1.464000000000E-02 + 42 8.000000000000E+00 1.245000000000E-02 + 42 9.000000000000E+00 1.082000000000E-02 + 42 1.000000000000E+01 9.559000000000E-03 + 43 1.000000000000E-03 8.697000000000E+05 + 43 1.500000000000E-03 3.390000000000E+05 + 43 2.000000000000E-03 1.687000000000E+05 + 43 2.677000000000E-03 8.153000000000E+04 + 43 2.677000100000E-03 2.936000000000E+05 + 43 2.734000000000E-03 2.760000000000E+05 + 43 2.793000000000E-03 2.596000000000E+05 + 43 2.793000100000E-03 3.606000000000E+05 + 43 3.000000000000E-03 3.019000000000E+05 + 43 3.043000000000E-03 2.918000000000E+05 + 43 3.043000100000E-03 3.339000000000E+05 + 43 4.000000000000E-03 1.677000000000E+05 + 43 5.000000000000E-03 9.421000000000E+04 + 43 6.000000000000E-03 5.824000000000E+04 + 43 8.000000000000E-03 2.689000000000E+04 + 43 1.000000000000E-02 1.462000000000E+04 + 43 1.500000000000E-02 4.745000000000E+03 + 43 2.000000000000E-02 2.110000000000E+03 + 43 2.104000000000E-02 1.827000000000E+03 + 43 2.104000010000E-02 1.199000000000E+04 + 43 3.000000000000E-02 4.754000000000E+03 + 43 4.000000000000E-02 2.164000000000E+03 + 43 5.000000000000E-02 1.161000000000E+03 + 43 6.000000000000E-02 6.920000000000E+02 + 43 8.000000000000E-02 3.024000000000E+02 + 43 1.000000000000E-01 1.578000000000E+02 + 43 1.500000000000E-01 4.806000000000E+01 + 43 2.000000000000E-01 2.071000000000E+01 + 43 3.000000000000E-01 6.477000000000E+00 + 43 4.000000000000E-01 2.940000000000E+00 + 43 5.000000000000E-01 1.641000000000E+00 + 43 6.000000000000E-01 1.044000000000E+00 + 43 8.000000000000E-01 5.374000000000E-01 + 43 1.000000000000E+00 3.350000000000E-01 + 43 1.022000000000E+00 3.188000000000E-01 + 43 1.250000000000E+00 2.151000000000E-01 + 43 1.500000000000E+00 1.544000000000E-01 + 43 2.000000000000E+00 9.428000000000E-02 + 43 2.044000000000E+00 9.099000000000E-02 + 43 3.000000000000E+00 5.035000000000E-02 + 43 4.000000000000E+00 3.353000000000E-02 + 43 5.000000000000E+00 2.489000000000E-02 + 43 6.000000000000E+00 1.970000000000E-02 + 43 7.000000000000E+00 1.626000000000E-02 + 43 8.000000000000E+00 1.382000000000E-02 + 43 9.000000000000E+00 1.201000000000E-02 + 43 1.000000000000E+01 1.061000000000E-02 + 44 1.000000000000E-03 9.584000000000E+05 + 44 1.500000000000E-03 3.749000000000E+05 + 44 2.000000000000E-03 1.868000000000E+05 + 44 2.838000000000E-03 7.800000000000E+04 + 44 2.838000100000E-03 2.749000000000E+05 + 44 2.902000000000E-03 2.584000000000E+05 + 44 2.967000000000E-03 2.428000000000E+05 + 44 2.967000100000E-03 3.305000000000E+05 + 44 3.000000000000E-03 3.285000000000E+05 + 44 3.224000000000E-03 2.739000000000E+05 + 44 3.224000100000E-03 3.136000000000E+05 + 44 4.000000000000E-03 1.829000000000E+05 + 44 5.000000000000E-03 1.028000000000E+05 + 44 6.000000000000E-03 6.368000000000E+04 + 44 8.000000000000E-03 2.946000000000E+04 + 44 1.000000000000E-02 1.604000000000E+04 + 44 1.500000000000E-02 5.217000000000E+03 + 44 2.000000000000E-02 2.324000000000E+03 + 44 2.212000000000E-02 1.748000000000E+03 + 44 2.212000010000E-02 1.137000000000E+04 + 44 3.000000000000E-02 5.152000000000E+03 + 44 4.000000000000E-02 2.352000000000E+03 + 44 5.000000000000E-02 1.264000000000E+03 + 44 6.000000000000E-02 7.557000000000E+02 + 44 8.000000000000E-02 3.313000000000E+02 + 44 1.000000000000E-01 1.733000000000E+02 + 44 1.500000000000E-01 5.292000000000E+01 + 44 2.000000000000E-01 2.285000000000E+01 + 44 3.000000000000E-01 7.166000000000E+00 + 44 4.000000000000E-01 3.258000000000E+00 + 44 5.000000000000E-01 1.821000000000E+00 + 44 6.000000000000E-01 1.159000000000E+00 + 44 8.000000000000E-01 5.968000000000E-01 + 44 1.000000000000E+00 3.722000000000E-01 + 44 1.022000000000E+00 3.541000000000E-01 + 44 1.250000000000E+00 2.389000000000E-01 + 44 1.500000000000E+00 1.714000000000E-01 + 44 2.000000000000E+00 1.046000000000E-01 + 44 2.044000000000E+00 1.010000000000E-01 + 44 3.000000000000E+00 5.584000000000E-02 + 44 4.000000000000E+00 3.716000000000E-02 + 44 5.000000000000E+00 2.757000000000E-02 + 44 6.000000000000E+00 2.182000000000E-02 + 44 7.000000000000E+00 1.800000000000E-02 + 44 8.000000000000E+00 1.530000000000E-02 + 44 9.000000000000E+00 1.329000000000E-02 + 44 1.000000000000E+01 1.174000000000E-02 + 45 1.000000000000E-03 1.053000000000E+06 + 45 1.500000000000E-03 4.134000000000E+05 + 45 2.000000000000E-03 2.063000000000E+05 + 45 3.000000000000E-03 7.491000000000E+04 + 45 3.004000000000E-03 7.466000000000E+04 + 45 3.004000100000E-03 2.576000000000E+05 + 45 3.074000000000E-03 2.420000000000E+05 + 45 3.146000000000E-03 2.276000000000E+05 + 45 3.146000100000E-03 3.146000000000E+05 + 45 3.276000000000E-03 2.846000000000E+05 + 45 3.412000000000E-03 2.575000000000E+05 + 45 3.412000100000E-03 2.949000000000E+05 + 45 4.000000000000E-03 1.991000000000E+05 + 45 5.000000000000E-03 1.118000000000E+05 + 45 6.000000000000E-03 6.942000000000E+04 + 45 8.000000000000E-03 3.220000000000E+04 + 45 1.000000000000E-02 1.756000000000E+04 + 45 1.500000000000E-02 5.725000000000E+03 + 45 2.000000000000E-02 2.555000000000E+03 + 45 2.322000000000E-02 1.674000000000E+03 + 45 2.322000010000E-02 1.079000000000E+04 + 45 3.000000000000E-02 5.567000000000E+03 + 45 4.000000000000E-02 2.550000000000E+03 + 45 5.000000000000E-02 1.375000000000E+03 + 45 6.000000000000E-02 8.236000000000E+02 + 45 8.000000000000E-02 3.620000000000E+02 + 45 1.000000000000E-01 1.898000000000E+02 + 45 1.500000000000E-01 5.814000000000E+01 + 45 2.000000000000E-01 2.515000000000E+01 + 45 3.000000000000E-01 7.908000000000E+00 + 45 4.000000000000E-01 3.600000000000E+00 + 45 5.000000000000E-01 2.014000000000E+00 + 45 6.000000000000E-01 1.284000000000E+00 + 45 8.000000000000E-01 6.613000000000E-01 + 45 1.000000000000E+00 4.124000000000E-01 + 45 1.022000000000E+00 3.924000000000E-01 + 45 1.250000000000E+00 2.647000000000E-01 + 45 1.500000000000E+00 1.899000000000E-01 + 45 2.000000000000E+00 1.159000000000E-01 + 45 2.044000000000E+00 1.118000000000E-01 + 45 3.000000000000E+00 6.179000000000E-02 + 45 4.000000000000E+00 4.110000000000E-02 + 45 5.000000000000E+00 3.048000000000E-02 + 45 6.000000000000E+00 2.411000000000E-02 + 45 7.000000000000E+00 1.989000000000E-02 + 45 8.000000000000E+00 1.690000000000E-02 + 45 9.000000000000E+00 1.468000000000E-02 + 45 1.000000000000E+01 1.296000000000E-02 + 46 1.000000000000E-03 1.154000000000E+06 + 46 1.500000000000E-03 4.544000000000E+05 + 46 2.000000000000E-03 2.271000000000E+05 + 46 3.000000000000E-03 8.252000000000E+04 + 46 3.173000000000E-03 7.155000000000E+04 + 46 3.173000100000E-03 2.385000000000E+05 + 46 3.251000000000E-03 2.258000000000E+05 + 46 3.330000000000E-03 2.138000000000E+05 + 46 3.330000100000E-03 2.931000000000E+05 + 46 3.465000000000E-03 2.668000000000E+05 + 46 3.604000000000E-03 2.428000000000E+05 + 46 3.604000100000E-03 2.786000000000E+05 + 46 4.000000000000E-03 2.159000000000E+05 + 46 5.000000000000E-03 1.214000000000E+05 + 46 6.000000000000E-03 7.543000000000E+04 + 46 8.000000000000E-03 3.510000000000E+04 + 46 1.000000000000E-02 1.917000000000E+04 + 46 1.500000000000E-02 6.264000000000E+03 + 46 2.000000000000E-02 2.798000000000E+03 + 46 2.435000000000E-02 1.604000000000E+03 + 46 2.435000010000E-02 1.026000000000E+04 + 46 3.000000000000E-02 5.993000000000E+03 + 46 4.000000000000E-02 2.759000000000E+03 + 46 5.000000000000E-02 1.492000000000E+03 + 46 6.000000000000E-02 8.955000000000E+02 + 46 8.000000000000E-02 3.945000000000E+02 + 46 1.000000000000E-01 2.073000000000E+02 + 46 1.500000000000E-01 6.370000000000E+01 + 46 2.000000000000E-01 2.763000000000E+01 + 46 3.000000000000E-01 8.706000000000E+00 + 46 4.000000000000E-01 3.969000000000E+00 + 46 5.000000000000E-01 2.224000000000E+00 + 46 6.000000000000E-01 1.418000000000E+00 + 46 8.000000000000E-01 7.308000000000E-01 + 46 1.000000000000E+00 4.558000000000E-01 + 46 1.022000000000E+00 4.338000000000E-01 + 46 1.250000000000E+00 2.927000000000E-01 + 46 1.500000000000E+00 2.099000000000E-01 + 46 2.000000000000E+00 1.280000000000E-01 + 46 2.044000000000E+00 1.235000000000E-01 + 46 3.000000000000E+00 6.822000000000E-02 + 46 4.000000000000E+00 4.535000000000E-02 + 46 5.000000000000E+00 3.362000000000E-02 + 46 6.000000000000E+00 2.658000000000E-02 + 46 7.000000000000E+00 2.192000000000E-02 + 46 8.000000000000E+00 1.863000000000E-02 + 46 9.000000000000E+00 1.617000000000E-02 + 46 1.000000000000E+01 1.428000000000E-02 + 47 1.000000000000E-03 1.259000000000E+06 + 47 1.500000000000E-03 4.985000000000E+05 + 47 2.000000000000E-03 2.496000000000E+05 + 47 3.000000000000E-03 9.090000000000E+04 + 47 3.351000000000E-03 6.858000000000E+04 + 47 3.351000100000E-03 2.272000000000E+05 + 47 3.436000000000E-03 2.135000000000E+05 + 47 3.524000000000E-03 2.006000000000E+05 + 47 3.524000100000E-03 2.760000000000E+05 + 47 3.662000000000E-03 2.512000000000E+05 + 47 3.806000000000E-03 2.286000000000E+05 + 47 3.806000100000E-03 2.619000000000E+05 + 47 4.000000000000E-03 2.328000000000E+05 + 47 5.000000000000E-03 1.315000000000E+05 + 47 6.000000000000E-03 8.186000000000E+04 + 47 8.000000000000E-03 3.819000000000E+04 + 47 1.000000000000E-02 2.089000000000E+04 + 47 1.500000000000E-02 6.846000000000E+03 + 47 2.000000000000E-02 3.063000000000E+03 + 47 2.551000000000E-02 1.540000000000E+03 + 47 2.551000010000E-02 9.756000000000E+03 + 47 3.000000000000E-02 6.434000000000E+03 + 47 4.000000000000E-02 2.983000000000E+03 + 47 5.000000000000E-02 1.617000000000E+03 + 47 6.000000000000E-02 9.717000000000E+02 + 47 8.000000000000E-02 4.292000000000E+02 + 47 1.000000000000E-01 2.259000000000E+02 + 47 1.500000000000E-01 6.966000000000E+01 + 47 2.000000000000E-01 3.026000000000E+01 + 47 3.000000000000E-01 9.564000000000E+00 + 47 4.000000000000E-01 4.368000000000E+00 + 47 5.000000000000E-01 2.449000000000E+00 + 47 6.000000000000E-01 1.562000000000E+00 + 47 8.000000000000E-01 8.059000000000E-01 + 47 1.000000000000E+00 5.028000000000E-01 + 47 1.022000000000E+00 4.784000000000E-01 + 47 1.250000000000E+00 3.228000000000E-01 + 47 1.500000000000E+00 2.314000000000E-01 + 47 2.000000000000E+00 1.411000000000E-01 + 47 2.044000000000E+00 1.361000000000E-01 + 47 3.000000000000E+00 7.512000000000E-02 + 47 4.000000000000E+00 4.991000000000E-02 + 47 5.000000000000E+00 3.699000000000E-02 + 47 6.000000000000E+00 2.924000000000E-02 + 47 7.000000000000E+00 2.411000000000E-02 + 47 8.000000000000E+00 2.048000000000E-02 + 47 9.000000000000E+00 1.778000000000E-02 + 47 1.000000000000E+01 1.570000000000E-02 + 48 1.000000000000E-03 1.371000000000E+06 + 48 1.500000000000E-03 5.457000000000E+05 + 48 2.000000000000E-03 2.737000000000E+05 + 48 3.000000000000E-03 9.990000000000E+04 + 48 3.537000000000E-03 6.568000000000E+04 + 48 3.537000100000E-03 2.140000000000E+05 + 48 3.631000000000E-03 2.007000000000E+05 + 48 3.727000000000E-03 1.881000000000E+05 + 48 3.727000100000E-03 2.583000000000E+05 + 48 4.000000000000E-03 2.173000000000E+05 + 48 4.018000000000E-03 2.149000000000E+05 + 48 4.018000100000E-03 2.466000000000E+05 + 48 5.000000000000E-03 1.426000000000E+05 + 48 6.000000000000E-03 8.871000000000E+04 + 48 8.000000000000E-03 4.147000000000E+04 + 48 1.000000000000E-02 2.273000000000E+04 + 48 1.500000000000E-02 7.469000000000E+03 + 48 2.000000000000E-02 3.348000000000E+03 + 48 2.671000000000E-02 1.480000000000E+03 + 48 2.671000010000E-02 9.289000000000E+03 + 48 3.000000000000E-02 6.885000000000E+03 + 48 4.000000000000E-02 3.217000000000E+03 + 48 5.000000000000E-02 1.747000000000E+03 + 48 6.000000000000E-02 1.052000000000E+03 + 48 8.000000000000E-02 4.662000000000E+02 + 48 1.000000000000E-01 2.458000000000E+02 + 48 1.500000000000E-01 7.602000000000E+01 + 48 2.000000000000E-01 3.310000000000E+01 + 48 3.000000000000E-01 1.048000000000E+01 + 48 4.000000000000E-01 4.796000000000E+00 + 48 5.000000000000E-01 2.691000000000E+00 + 48 6.000000000000E-01 1.718000000000E+00 + 48 8.000000000000E-01 8.870000000000E-01 + 48 1.000000000000E+00 5.535000000000E-01 + 48 1.022000000000E+00 5.267000000000E-01 + 48 1.250000000000E+00 3.553000000000E-01 + 48 1.500000000000E+00 2.546000000000E-01 + 48 2.000000000000E+00 1.552000000000E-01 + 48 2.044000000000E+00 1.498000000000E-01 + 48 3.000000000000E+00 8.259000000000E-02 + 48 4.000000000000E+00 5.484000000000E-02 + 48 5.000000000000E+00 4.062000000000E-02 + 48 6.000000000000E+00 3.210000000000E-02 + 48 7.000000000000E+00 2.646000000000E-02 + 48 8.000000000000E+00 2.247000000000E-02 + 48 9.000000000000E+00 1.951000000000E-02 + 48 1.000000000000E+01 1.723000000000E-02 + 49 1.000000000000E-03 1.487000000000E+06 + 49 1.500000000000E-03 5.955000000000E+05 + 49 2.000000000000E-03 2.995000000000E+05 + 49 3.000000000000E-03 1.096000000000E+05 + 49 3.730000000000E-03 6.291000000000E+04 + 49 3.730000100000E-03 1.984000000000E+05 + 49 3.833000000000E-03 1.871000000000E+05 + 49 3.938000000000E-03 1.765000000000E+05 + 49 3.938000100000E-03 2.377000000000E+05 + 49 4.000000000000E-03 2.336000000000E+05 + 49 4.237000000000E-03 2.023000000000E+05 + 49 4.237000100000E-03 2.322000000000E+05 + 49 5.000000000000E-03 1.542000000000E+05 + 49 6.000000000000E-03 9.591000000000E+04 + 49 8.000000000000E-03 4.495000000000E+04 + 49 1.000000000000E-02 2.468000000000E+04 + 49 1.500000000000E-02 8.131000000000E+03 + 49 2.000000000000E-02 3.649000000000E+03 + 49 2.794000000000E-02 1.422000000000E+03 + 49 2.794000010000E-02 8.860000000000E+03 + 49 3.000000000000E-02 7.379000000000E+03 + 49 4.000000000000E-02 3.465000000000E+03 + 49 5.000000000000E-02 1.883000000000E+03 + 49 6.000000000000E-02 1.136000000000E+03 + 49 8.000000000000E-02 5.050000000000E+02 + 49 1.000000000000E-01 2.668000000000E+02 + 49 1.500000000000E-01 8.278000000000E+01 + 49 2.000000000000E-01 3.611000000000E+01 + 49 3.000000000000E-01 1.147000000000E+01 + 49 4.000000000000E-01 5.255000000000E+00 + 49 5.000000000000E-01 2.952000000000E+00 + 49 6.000000000000E-01 1.886000000000E+00 + 49 8.000000000000E-01 9.741000000000E-01 + 49 1.000000000000E+00 6.080000000000E-01 + 49 1.022000000000E+00 5.785000000000E-01 + 49 1.250000000000E+00 3.902000000000E-01 + 49 1.500000000000E+00 2.796000000000E-01 + 49 2.000000000000E+00 1.704000000000E-01 + 49 2.044000000000E+00 1.644000000000E-01 + 49 3.000000000000E+00 9.059000000000E-02 + 49 4.000000000000E+00 6.012000000000E-02 + 49 5.000000000000E+00 4.452000000000E-02 + 49 6.000000000000E+00 3.517000000000E-02 + 49 7.000000000000E+00 2.899000000000E-02 + 49 8.000000000000E+00 2.461000000000E-02 + 49 9.000000000000E+00 2.136000000000E-02 + 49 1.000000000000E+01 1.886000000000E-02 + 50 1.000000000000E-03 1.606000000000E+06 + 50 1.500000000000E-03 6.482000000000E+05 + 50 2.000000000000E-03 3.268000000000E+05 + 50 3.000000000000E-03 1.198000000000E+05 + 50 3.929000000000E-03 6.031000000000E+04 + 50 3.929000100000E-03 1.814000000000E+05 + 50 4.000000000000E-03 1.841000000000E+05 + 50 4.156000000000E-03 1.659000000000E+05 + 50 4.156000100000E-03 2.246000000000E+05 + 50 4.308000000000E-03 2.068000000000E+05 + 50 4.465000000000E-03 1.904000000000E+05 + 50 4.465000100000E-03 2.192000000000E+05 + 50 5.000000000000E-03 1.660000000000E+05 + 50 6.000000000000E-03 1.035000000000E+05 + 50 8.000000000000E-03 4.864000000000E+04 + 50 1.000000000000E-02 2.675000000000E+04 + 50 1.500000000000E-02 8.835000000000E+03 + 50 2.000000000000E-02 3.971000000000E+03 + 50 2.920000000000E-02 1.368000000000E+03 + 50 2.920000010000E-02 8.430000000000E+03 + 50 3.000000000000E-02 7.968000000000E+03 + 50 4.000000000000E-02 3.719000000000E+03 + 50 5.000000000000E-02 2.024000000000E+03 + 50 6.000000000000E-02 1.225000000000E+03 + 50 8.000000000000E-02 5.460000000000E+02 + 50 1.000000000000E-01 2.891000000000E+02 + 50 1.500000000000E-01 8.996000000000E+01 + 50 2.000000000000E-01 3.933000000000E+01 + 50 3.000000000000E-01 1.252000000000E+01 + 50 4.000000000000E-01 5.746000000000E+00 + 50 5.000000000000E-01 3.231000000000E+00 + 50 6.000000000000E-01 2.066000000000E+00 + 50 8.000000000000E-01 1.068000000000E+00 + 50 1.000000000000E+00 6.664000000000E-01 + 50 1.022000000000E+00 6.342000000000E-01 + 50 1.250000000000E+00 4.277000000000E-01 + 50 1.500000000000E+00 3.064000000000E-01 + 50 2.000000000000E+00 1.866000000000E-01 + 50 2.044000000000E+00 1.801000000000E-01 + 50 3.000000000000E+00 9.918000000000E-02 + 50 4.000000000000E+00 6.578000000000E-02 + 50 5.000000000000E+00 4.870000000000E-02 + 50 6.000000000000E+00 3.846000000000E-02 + 50 7.000000000000E+00 3.169000000000E-02 + 50 8.000000000000E+00 2.690000000000E-02 + 50 9.000000000000E+00 2.335000000000E-02 + 50 1.000000000000E+01 2.061000000000E-02 + 51 1.000000000000E-03 1.734000000000E+06 + 51 1.500000000000E-03 7.042000000000E+05 + 51 2.000000000000E-03 3.558000000000E+05 + 51 3.000000000000E-03 1.308000000000E+05 + 51 4.000000000000E-03 6.295000000000E+04 + 51 4.132000000000E-03 5.791000000000E+04 + 51 4.132000100000E-03 1.747000000000E+05 + 51 4.254000000000E-03 1.652000000000E+05 + 51 4.380000000000E-03 1.561000000000E+05 + 51 4.380000100000E-03 2.112000000000E+05 + 51 4.537000000000E-03 1.948000000000E+05 + 51 4.698000000000E-03 1.797000000000E+05 + 51 4.698000100000E-03 2.071000000000E+05 + 51 5.000000000000E-03 1.779000000000E+05 + 51 6.000000000000E-03 1.117000000000E+05 + 51 8.000000000000E-03 5.251000000000E+04 + 51 1.000000000000E-02 2.893000000000E+04 + 51 1.500000000000E-02 9.580000000000E+03 + 51 2.000000000000E-02 4.314000000000E+03 + 51 3.000000000000E-02 1.379000000000E+03 + 51 3.049000000000E-02 1.317000000000E+03 + 51 3.049000010000E-02 8.074000000000E+03 + 51 4.000000000000E-02 3.982000000000E+03 + 51 5.000000000000E-02 2.176000000000E+03 + 51 6.000000000000E-02 1.318000000000E+03 + 51 8.000000000000E-02 5.891000000000E+02 + 51 1.000000000000E-01 3.125000000000E+02 + 51 1.500000000000E-01 9.760000000000E+01 + 51 2.000000000000E-01 4.274000000000E+01 + 51 3.000000000000E-01 1.364000000000E+01 + 51 4.000000000000E-01 6.271000000000E+00 + 51 5.000000000000E-01 3.530000000000E+00 + 51 6.000000000000E-01 2.257000000000E+00 + 51 8.000000000000E-01 1.168000000000E+00 + 51 1.000000000000E+00 7.292000000000E-01 + 51 1.022000000000E+00 6.940000000000E-01 + 51 1.250000000000E+00 4.680000000000E-01 + 51 1.500000000000E+00 3.352000000000E-01 + 51 2.000000000000E+00 2.041000000000E-01 + 51 2.044000000000E+00 1.969000000000E-01 + 51 3.000000000000E+00 1.084000000000E-01 + 51 4.000000000000E+00 7.186000000000E-02 + 51 5.000000000000E+00 5.317000000000E-02 + 51 6.000000000000E+00 4.198000000000E-02 + 51 7.000000000000E+00 3.458000000000E-02 + 51 8.000000000000E+00 2.935000000000E-02 + 51 9.000000000000E+00 2.547000000000E-02 + 51 1.000000000000E+01 2.248000000000E-02 + 52 1.000000000000E-03 1.785000000000E+06 + 52 1.003000000000E-03 1.774000000000E+06 + 52 1.006000000000E-03 1.763000000000E+06 + 52 1.006000100000E-03 1.838000000000E+06 + 52 1.500000000000E-03 7.628000000000E+05 + 52 2.000000000000E-03 3.867000000000E+05 + 52 3.000000000000E-03 1.426000000000E+05 + 52 4.000000000000E-03 6.871000000000E+04 + 52 4.341000000000E-03 5.565000000000E+04 + 52 4.341000100000E-03 1.659000000000E+05 + 52 4.475000000000E-03 1.563000000000E+05 + 52 4.612000000000E-03 1.472000000000E+05 + 52 4.612000100000E-03 1.991000000000E+05 + 52 4.773000000000E-03 1.839000000000E+05 + 52 4.939000000000E-03 1.698000000000E+05 + 52 4.939000100000E-03 1.959000000000E+05 + 52 5.000000000000E-03 1.900000000000E+05 + 52 6.000000000000E-03 1.203000000000E+05 + 52 8.000000000000E-03 5.654000000000E+04 + 52 1.000000000000E-02 3.123000000000E+04 + 52 1.500000000000E-02 1.037000000000E+04 + 52 2.000000000000E-02 4.678000000000E+03 + 52 3.000000000000E-02 1.499000000000E+03 + 52 3.181000000000E-02 1.269000000000E+03 + 52 3.181000010000E-02 7.723000000000E+03 + 52 4.000000000000E-02 4.255000000000E+03 + 52 5.000000000000E-02 2.334000000000E+03 + 52 6.000000000000E-02 1.417000000000E+03 + 52 8.000000000000E-02 6.348000000000E+02 + 52 1.000000000000E-01 3.373000000000E+02 + 52 1.500000000000E-01 1.057000000000E+02 + 52 2.000000000000E-01 4.637000000000E+01 + 52 3.000000000000E-01 1.484000000000E+01 + 52 4.000000000000E-01 6.831000000000E+00 + 52 5.000000000000E-01 3.849000000000E+00 + 52 6.000000000000E-01 2.464000000000E+00 + 52 8.000000000000E-01 1.275000000000E+00 + 52 1.000000000000E+00 7.965000000000E-01 + 52 1.022000000000E+00 7.580000000000E-01 + 52 1.250000000000E+00 5.112000000000E-01 + 52 1.500000000000E+00 3.660000000000E-01 + 52 2.000000000000E+00 2.228000000000E-01 + 52 2.044000000000E+00 2.149000000000E-01 + 52 3.000000000000E+00 1.182000000000E-01 + 52 4.000000000000E+00 7.834000000000E-02 + 52 5.000000000000E+00 5.795000000000E-02 + 52 6.000000000000E+00 4.574000000000E-02 + 52 7.000000000000E+00 3.767000000000E-02 + 52 8.000000000000E+00 3.197000000000E-02 + 52 9.000000000000E+00 2.773000000000E-02 + 52 1.000000000000E+01 2.447000000000E-02 + 53 1.000000000000E-03 1.915000000000E+06 + 53 1.035000000000E-03 1.781000000000E+06 + 53 1.072000000000E-03 1.656000000000E+06 + 53 1.072000100000E-03 1.727000000000E+06 + 53 1.500000000000E-03 8.242000000000E+05 + 53 2.000000000000E-03 4.192000000000E+05 + 53 3.000000000000E-03 1.550000000000E+05 + 53 4.000000000000E-03 7.483000000000E+04 + 53 4.557000000000E-03 5.350000000000E+04 + 53 4.557000100000E-03 1.580000000000E+05 + 53 4.702000000000E-03 1.481000000000E+05 + 53 4.852000000000E-03 1.388000000000E+05 + 53 4.852000100000E-03 1.872000000000E+05 + 53 5.000000000000E-03 1.766000000000E+05 + 53 5.188000000000E-03 1.605000000000E+05 + 53 5.188000100000E-03 1.852000000000E+05 + 53 6.000000000000E-03 1.292000000000E+05 + 53 8.000000000000E-03 6.082000000000E+04 + 53 1.000000000000E-02 3.366000000000E+04 + 53 1.500000000000E-02 1.121000000000E+04 + 53 2.000000000000E-02 5.065000000000E+03 + 53 3.000000000000E-02 1.626000000000E+03 + 53 3.317000000000E-02 1.224000000000E+03 + 53 3.317000010000E-02 7.393000000000E+03 + 53 4.000000000000E-02 4.532000000000E+03 + 53 5.000000000000E-02 2.501000000000E+03 + 53 6.000000000000E-02 1.519000000000E+03 + 53 8.000000000000E-02 6.828000000000E+02 + 53 1.000000000000E-01 3.635000000000E+02 + 53 1.500000000000E-01 1.142000000000E+02 + 53 2.000000000000E-01 5.022000000000E+01 + 53 3.000000000000E-01 1.612000000000E+01 + 53 4.000000000000E-01 7.429000000000E+00 + 53 5.000000000000E-01 4.189000000000E+00 + 53 6.000000000000E-01 2.684000000000E+00 + 53 8.000000000000E-01 1.390000000000E+00 + 53 1.000000000000E+00 8.683000000000E-01 + 53 1.022000000000E+00 8.265000000000E-01 + 53 1.250000000000E+00 5.573000000000E-01 + 53 1.500000000000E+00 3.989000000000E-01 + 53 2.000000000000E+00 2.428000000000E-01 + 53 2.044000000000E+00 2.342000000000E-01 + 53 3.000000000000E+00 1.288000000000E-01 + 53 4.000000000000E+00 8.527000000000E-02 + 53 5.000000000000E+00 6.305000000000E-02 + 53 6.000000000000E+00 4.975000000000E-02 + 53 7.000000000000E+00 4.097000000000E-02 + 53 8.000000000000E+00 3.476000000000E-02 + 53 9.000000000000E+00 3.015000000000E-02 + 53 1.000000000000E+01 2.660000000000E-02 + 54 1.000000000000E-03 2.050000000000E+06 + 54 1.072000000000E-03 1.773000000000E+06 + 54 1.149000000000E-03 1.533000000000E+06 + 54 1.149000100000E-03 1.599000000000E+06 + 54 1.500000000000E-03 8.888000000000E+05 + 54 2.000000000000E-03 4.535000000000E+05 + 54 3.000000000000E-03 1.682000000000E+05 + 54 4.000000000000E-03 8.132000000000E+04 + 54 4.782000000000E-03 5.139000000000E+04 + 54 4.782000100000E-03 1.502000000000E+05 + 54 5.000000000000E-03 1.383000000000E+05 + 54 5.104000000000E-03 1.307000000000E+05 + 54 5.104000100000E-03 1.773000000000E+05 + 54 5.275000000000E-03 1.638000000000E+05 + 54 5.453000000000E-03 1.514000000000E+05 + 54 5.453000100000E-03 1.748000000000E+05 + 54 6.000000000000E-03 1.380000000000E+05 + 54 8.000000000000E-03 6.535000000000E+04 + 54 1.000000000000E-02 3.623000000000E+04 + 54 1.500000000000E-02 1.210000000000E+04 + 54 2.000000000000E-02 5.473000000000E+03 + 54 3.000000000000E-02 1.761000000000E+03 + 54 3.456000000000E-02 1.181000000000E+03 + 54 3.456000010000E-02 7.073000000000E+03 + 54 4.000000000000E-02 4.820000000000E+03 + 54 5.000000000000E-02 2.675000000000E+03 + 54 6.000000000000E-02 1.625000000000E+03 + 54 8.000000000000E-02 7.331000000000E+02 + 54 1.000000000000E-01 3.910000000000E+02 + 54 1.500000000000E-01 1.232000000000E+02 + 54 2.000000000000E-01 5.429000000000E+01 + 54 3.000000000000E-01 1.746000000000E+01 + 54 4.000000000000E-01 8.064000000000E+00 + 54 5.000000000000E-01 4.553000000000E+00 + 54 6.000000000000E-01 2.918000000000E+00 + 54 8.000000000000E-01 1.513000000000E+00 + 54 1.000000000000E+00 9.451000000000E-01 + 54 1.022000000000E+00 8.995000000000E-01 + 54 1.250000000000E+00 6.066000000000E-01 + 54 1.500000000000E+00 4.341000000000E-01 + 54 2.000000000000E+00 2.641000000000E-01 + 54 2.044000000000E+00 2.548000000000E-01 + 54 3.000000000000E+00 1.400000000000E-01 + 54 4.000000000000E+00 9.266000000000E-02 + 54 5.000000000000E+00 6.849000000000E-02 + 54 6.000000000000E+00 5.403000000000E-02 + 54 7.000000000000E+00 4.448000000000E-02 + 54 8.000000000000E+00 3.773000000000E-02 + 54 9.000000000000E+00 3.273000000000E-02 + 54 1.000000000000E+01 2.887000000000E-02 + 55 1.000000000000E-03 2.065000000000E+06 + 55 1.032000000000E-03 1.934000000000E+06 + 55 1.065000000000E-03 1.811000000000E+06 + 55 1.065000100000E-03 1.915000000000E+06 + 55 1.139000000000E-03 1.667000000000E+06 + 55 1.217000000000E-03 1.452000000000E+06 + 55 1.217000100000E-03 1.519000000000E+06 + 55 1.500000000000E-03 9.550000000000E+05 + 55 2.000000000000E-03 4.895000000000E+05 + 55 3.000000000000E-03 1.821000000000E+05 + 55 4.000000000000E-03 8.821000000000E+04 + 55 5.000000000000E-03 4.972000000000E+04 + 55 5.012000000000E-03 4.943000000000E+04 + 55 5.012000100000E-03 1.462000000000E+05 + 55 5.183000000000E-03 1.344000000000E+05 + 55 5.359000000000E-03 1.235000000000E+05 + 55 5.359000100000E-03 1.687000000000E+05 + 55 5.534000000000E-03 1.557000000000E+05 + 55 5.714000000000E-03 1.437000000000E+05 + 55 5.714000100000E-03 1.655000000000E+05 + 55 6.000000000000E-03 1.471000000000E+05 + 55 8.000000000000E-03 7.015000000000E+04 + 55 1.000000000000E-02 3.892000000000E+04 + 55 1.500000000000E-02 1.304000000000E+04 + 55 2.000000000000E-02 5.909000000000E+03 + 55 3.000000000000E-02 1.905000000000E+03 + 55 3.598000000000E-02 1.140000000000E+03 + 55 3.598000010000E-02 6.783000000000E+03 + 55 4.000000000000E-02 5.119000000000E+03 + 55 5.000000000000E-02 2.853000000000E+03 + 55 6.000000000000E-02 1.737000000000E+03 + 55 8.000000000000E-02 7.856000000000E+02 + 55 1.000000000000E-01 4.200000000000E+02 + 55 1.500000000000E-01 1.328000000000E+02 + 55 2.000000000000E-01 5.861000000000E+01 + 55 3.000000000000E-01 1.890000000000E+01 + 55 4.000000000000E-01 8.741000000000E+00 + 55 5.000000000000E-01 4.940000000000E+00 + 55 6.000000000000E-01 3.168000000000E+00 + 55 8.000000000000E-01 1.644000000000E+00 + 55 1.000000000000E+00 1.027000000000E+00 + 55 1.022000000000E+00 9.828000000000E-01 + 55 1.250000000000E+00 6.627000000000E-01 + 55 1.500000000000E+00 4.741000000000E-01 + 55 2.000000000000E+00 2.884000000000E-01 + 55 2.044000000000E+00 2.782000000000E-01 + 55 3.000000000000E+00 1.527000000000E-01 + 55 4.000000000000E+00 1.011000000000E-01 + 55 5.000000000000E+00 7.467000000000E-02 + 55 6.000000000000E+00 5.889000000000E-02 + 55 7.000000000000E+00 4.847000000000E-02 + 55 8.000000000000E+00 4.111000000000E-02 + 55 9.000000000000E+00 3.565000000000E-02 + 55 1.000000000000E+01 3.145000000000E-02 + 56 1.000000000000E-03 1.946000000000E+06 + 56 1.031000000000E-03 1.819000000000E+06 + 56 1.062000000000E-03 1.701000000000E+06 + 56 1.062000100000E-03 1.947000000000E+06 + 56 1.099000000000E-03 1.812000000000E+06 + 56 1.137000000000E-03 1.687000000000E+06 + 56 1.137000100000E-03 1.785000000000E+06 + 56 1.212000000000E-03 1.560000000000E+06 + 56 1.293000000000E-03 1.364000000000E+06 + 56 1.293000100000E-03 1.425000000000E+06 + 56 1.500000000000E-03 1.024000000000E+06 + 56 2.000000000000E-03 5.270000000000E+05 + 56 3.000000000000E-03 1.968000000000E+05 + 56 4.000000000000E-03 9.550000000000E+04 + 56 5.000000000000E-03 5.389000000000E+04 + 56 5.247000000000E-03 4.757000000000E+04 + 56 5.247000100000E-03 1.379000000000E+05 + 56 5.432000000000E-03 1.269000000000E+05 + 56 5.624000000000E-03 1.168000000000E+05 + 56 5.624000100000E-03 1.589000000000E+05 + 56 5.803000000000E-03 1.471000000000E+05 + 56 5.989000000000E-03 1.361000000000E+05 + 56 5.989000100000E-03 1.571000000000E+05 + 56 6.000000000000E-03 1.563000000000E+05 + 56 8.000000000000E-03 7.522000000000E+04 + 56 1.000000000000E-02 4.174000000000E+04 + 56 1.500000000000E-02 1.402000000000E+04 + 56 2.000000000000E-02 6.368000000000E+03 + 56 3.000000000000E-02 2.057000000000E+03 + 56 3.744000000000E-02 1.102000000000E+03 + 56 3.744000010000E-02 6.504000000000E+03 + 56 4.000000000000E-02 5.463000000000E+03 + 56 5.000000000000E-02 3.037000000000E+03 + 56 6.000000000000E-02 1.854000000000E+03 + 56 8.000000000000E-02 8.406000000000E+02 + 56 1.000000000000E-01 4.502000000000E+02 + 56 1.500000000000E-01 1.428000000000E+02 + 56 2.000000000000E-01 6.317000000000E+01 + 56 3.000000000000E-01 2.042000000000E+01 + 56 4.000000000000E-01 9.459000000000E+00 + 56 5.000000000000E-01 5.352000000000E+00 + 56 6.000000000000E-01 3.435000000000E+00 + 56 8.000000000000E-01 1.783000000000E+00 + 56 1.000000000000E+00 1.114000000000E+00 + 56 1.022000000000E+00 1.066000000000E+00 + 56 1.250000000000E+00 7.190000000000E-01 + 56 1.500000000000E+00 5.144000000000E-01 + 56 2.000000000000E+00 3.127000000000E-01 + 56 2.044000000000E+00 3.017000000000E-01 + 56 3.000000000000E+00 1.656000000000E-01 + 56 4.000000000000E+00 1.095000000000E-01 + 56 5.000000000000E+00 8.087000000000E-02 + 56 6.000000000000E+00 6.376000000000E-02 + 56 7.000000000000E+00 5.247000000000E-02 + 56 8.000000000000E+00 4.449000000000E-02 + 56 9.000000000000E+00 3.858000000000E-02 + 56 1.000000000000E+01 3.403000000000E-02 + 57 1.000000000000E-03 2.094000000000E+06 + 57 1.060000000000E-03 1.838000000000E+06 + 57 1.123000000000E-03 1.613000000000E+06 + 57 1.123000100000E-03 1.849000000000E+06 + 57 1.163000000000E-03 1.716000000000E+06 + 57 1.204000000000E-03 1.592000000000E+06 + 57 1.204000100000E-03 1.687000000000E+06 + 57 1.280000000000E-03 1.482000000000E+06 + 57 1.361000000000E-03 1.302000000000E+06 + 57 1.361000100000E-03 1.360000000000E+06 + 57 1.500000000000E-03 1.099000000000E+06 + 57 2.000000000000E-03 5.666000000000E+05 + 57 3.000000000000E-03 2.122000000000E+05 + 57 4.000000000000E-03 1.031000000000E+05 + 57 5.000000000000E-03 5.828000000000E+04 + 57 5.483000000000E-03 4.591000000000E+04 + 57 5.483000100000E-03 1.318000000000E+05 + 57 5.683000000000E-03 1.208000000000E+05 + 57 5.891000000000E-03 1.107000000000E+05 + 57 5.891000100000E-03 1.500000000000E+05 + 57 6.000000000000E-03 1.447000000000E+05 + 57 6.266000000000E-03 1.294000000000E+05 + 57 6.266000100000E-03 1.493000000000E+05 + 57 8.000000000000E-03 8.054000000000E+04 + 57 1.000000000000E-02 4.468000000000E+04 + 57 1.500000000000E-02 1.506000000000E+04 + 57 2.000000000000E-02 6.850000000000E+03 + 57 3.000000000000E-02 2.218000000000E+03 + 57 3.892000000000E-02 1.065000000000E+03 + 57 3.892000010000E-02 6.245000000000E+03 + 57 4.000000000000E-02 5.802000000000E+03 + 57 5.000000000000E-02 3.227000000000E+03 + 57 6.000000000000E-02 1.977000000000E+03 + 57 8.000000000000E-02 8.981000000000E+02 + 57 1.000000000000E-01 4.820000000000E+02 + 57 1.500000000000E-01 1.534000000000E+02 + 57 2.000000000000E-01 6.798000000000E+01 + 57 3.000000000000E-01 2.203000000000E+01 + 57 4.000000000000E-01 1.022000000000E+01 + 57 5.000000000000E-01 5.789000000000E+00 + 57 6.000000000000E-01 3.718000000000E+00 + 57 8.000000000000E-01 1.932000000000E+00 + 57 1.000000000000E+00 1.208000000000E+00 + 57 1.022000000000E+00 1.155000000000E+00 + 57 1.250000000000E+00 7.791000000000E-01 + 57 1.500000000000E+00 5.572000000000E-01 + 57 2.000000000000E+00 3.387000000000E-01 + 57 2.044000000000E+00 3.267000000000E-01 + 57 3.000000000000E+00 1.792000000000E-01 + 57 4.000000000000E+00 1.184000000000E-01 + 57 5.000000000000E+00 8.745000000000E-02 + 57 6.000000000000E+00 6.893000000000E-02 + 57 7.000000000000E+00 5.671000000000E-02 + 57 8.000000000000E+00 4.808000000000E-02 + 57 9.000000000000E+00 4.169000000000E-02 + 57 1.000000000000E+01 3.676000000000E-02 + 58 1.000000000000E-03 2.257000000000E+06 + 58 1.089000000000E-03 1.856000000000E+06 + 58 1.185000000000E-03 1.525000000000E+06 + 58 1.185000100000E-03 1.753000000000E+06 + 58 1.228000000000E-03 1.623000000000E+06 + 58 1.273000000000E-03 1.502000000000E+06 + 58 1.273000100000E-03 1.592000000000E+06 + 58 1.352000000000E-03 1.399000000000E+06 + 58 1.437000000000E-03 1.230000000000E+06 + 58 1.437000100000E-03 1.285000000000E+06 + 58 1.500000000000E-03 1.169000000000E+06 + 58 2.000000000000E-03 6.048000000000E+05 + 58 3.000000000000E-03 2.277000000000E+05 + 58 4.000000000000E-03 1.105000000000E+05 + 58 5.000000000000E-03 6.250000000000E+04 + 58 5.723000000000E-03 4.407000000000E+04 + 58 5.723000100000E-03 1.258000000000E+05 + 58 6.000000000000E-03 1.131000000000E+05 + 58 6.164000000000E-03 1.049000000000E+05 + 58 6.164000100000E-03 1.429000000000E+05 + 58 6.354000000000E-03 1.326000000000E+05 + 58 6.549000000000E-03 1.230000000000E+05 + 58 6.549000100000E-03 1.420000000000E+05 + 58 8.000000000000E-03 8.595000000000E+04 + 58 1.000000000000E-02 4.771000000000E+04 + 58 1.500000000000E-02 1.613000000000E+04 + 58 2.000000000000E-02 7.350000000000E+03 + 58 3.000000000000E-02 2.385000000000E+03 + 58 4.000000000000E-02 1.062000000000E+03 + 58 4.044000000000E-02 1.030000000000E+03 + 58 4.044000010000E-02 5.981000000000E+03 + 58 5.000000000000E-02 3.422000000000E+03 + 58 6.000000000000E-02 2.105000000000E+03 + 58 8.000000000000E-02 9.583000000000E+02 + 58 1.000000000000E-01 5.152000000000E+02 + 58 1.500000000000E-01 1.645000000000E+02 + 58 2.000000000000E-01 7.304000000000E+01 + 58 3.000000000000E-01 2.373000000000E+01 + 58 4.000000000000E-01 1.103000000000E+01 + 58 5.000000000000E-01 6.251000000000E+00 + 58 6.000000000000E-01 4.017000000000E+00 + 58 8.000000000000E-01 2.089000000000E+00 + 58 1.000000000000E+00 1.306000000000E+00 + 58 1.022000000000E+00 1.250000000000E+00 + 58 1.250000000000E+00 8.427000000000E-01 + 58 1.500000000000E+00 6.026000000000E-01 + 58 2.000000000000E+00 3.662000000000E-01 + 58 2.044000000000E+00 3.532000000000E-01 + 58 3.000000000000E+00 1.936000000000E-01 + 58 4.000000000000E+00 1.279000000000E-01 + 58 5.000000000000E+00 9.442000000000E-02 + 58 6.000000000000E+00 7.440000000000E-02 + 58 7.000000000000E+00 6.120000000000E-02 + 58 8.000000000000E+00 5.188000000000E-02 + 58 9.000000000000E+00 4.497000000000E-02 + 58 1.000000000000E+01 3.966000000000E-02 + 59 1.000000000000E-03 2.474000000000E+06 + 59 1.115000000000E-03 1.905000000000E+06 + 59 1.242000000000E-03 1.467000000000E+06 + 59 1.242000100000E-03 1.688000000000E+06 + 59 1.289000000000E-03 1.556000000000E+06 + 59 1.337000000000E-03 1.435000000000E+06 + 59 1.337000100000E-03 1.522000000000E+06 + 59 1.500000000000E-03 1.189000000000E+06 + 59 1.511000000000E-03 1.170000000000E+06 + 59 1.511000100000E-03 1.223000000000E+06 + 59 2.000000000000E-03 6.457000000000E+05 + 59 3.000000000000E-03 2.432000000000E+05 + 59 4.000000000000E-03 1.186000000000E+05 + 59 5.000000000000E-03 6.712000000000E+04 + 59 5.964000000000E-03 4.254000000000E+04 + 59 5.964000100000E-03 1.198000000000E+05 + 59 6.000000000000E-03 1.192000000000E+05 + 59 6.440000000000E-03 9.974000000000E+04 + 59 6.440000100000E-03 1.359000000000E+05 + 59 6.635000000000E-03 1.263000000000E+05 + 59 6.835000000000E-03 1.173000000000E+05 + 59 6.835000100000E-03 1.355000000000E+05 + 59 8.000000000000E-03 9.149000000000E+04 + 59 1.000000000000E-02 5.092000000000E+04 + 59 1.500000000000E-02 1.727000000000E+04 + 59 2.000000000000E-02 7.879000000000E+03 + 59 3.000000000000E-02 2.562000000000E+03 + 59 4.000000000000E-02 1.143000000000E+03 + 59 4.199000000000E-02 9.964000000000E+02 + 59 4.199000010000E-02 5.743000000000E+03 + 59 5.000000000000E-02 3.621000000000E+03 + 59 6.000000000000E-02 2.238000000000E+03 + 59 8.000000000000E-02 1.021000000000E+03 + 59 1.000000000000E-01 5.501000000000E+02 + 59 1.500000000000E-01 1.761000000000E+02 + 59 2.000000000000E-01 7.836000000000E+01 + 59 3.000000000000E-01 2.553000000000E+01 + 59 4.000000000000E-01 1.188000000000E+01 + 59 5.000000000000E-01 6.742000000000E+00 + 59 6.000000000000E-01 4.336000000000E+00 + 59 8.000000000000E-01 2.256000000000E+00 + 59 1.000000000000E+00 1.411000000000E+00 + 59 1.022000000000E+00 1.350000000000E+00 + 59 1.250000000000E+00 9.103000000000E-01 + 59 1.500000000000E+00 6.508000000000E-01 + 59 2.000000000000E+00 3.954000000000E-01 + 59 2.044000000000E+00 3.814000000000E-01 + 59 3.000000000000E+00 2.089000000000E-01 + 59 4.000000000000E+00 1.380000000000E-01 + 59 5.000000000000E+00 1.018000000000E-01 + 59 6.000000000000E+00 8.021000000000E-02 + 59 7.000000000000E+00 6.596000000000E-02 + 59 8.000000000000E+00 5.591000000000E-02 + 59 9.000000000000E+00 4.846000000000E-02 + 59 1.000000000000E+01 4.272000000000E-02 + 60 1.000000000000E-03 1.585000000000E+06 + 60 1.002000000000E-03 1.622000000000E+06 + 60 1.005000000000E-03 1.658000000000E+06 + 60 1.005000100000E-03 2.039000000000E+06 + 60 1.142000000000E-03 1.701000000000E+06 + 60 1.297000000000E-03 1.419000000000E+06 + 60 1.297000100000E-03 1.634000000000E+06 + 60 1.349000000000E-03 1.498000000000E+06 + 60 1.403000000000E-03 1.374000000000E+06 + 60 1.403000100000E-03 1.457000000000E+06 + 60 1.500000000000E-03 1.261000000000E+06 + 60 1.575000000000E-03 1.133000000000E+06 + 60 1.575000100000E-03 1.184000000000E+06 + 60 2.000000000000E-03 6.874000000000E+05 + 60 3.000000000000E-03 2.601000000000E+05 + 60 4.000000000000E-03 1.270000000000E+05 + 60 5.000000000000E-03 7.196000000000E+04 + 60 6.000000000000E-03 4.494000000000E+04 + 60 6.208000000000E-03 4.113000000000E+04 + 60 6.208000100000E-03 1.160000000000E+05 + 60 6.460000000000E-03 1.050000000000E+05 + 60 6.722000000000E-03 9.501000000000E+04 + 60 6.722000100000E-03 1.296000000000E+05 + 60 6.921000000000E-03 1.205000000000E+05 + 60 7.126000000000E-03 1.121000000000E+05 + 60 7.126000100000E-03 1.294000000000E+05 + 60 8.000000000000E-03 9.709000000000E+04 + 60 1.000000000000E-02 5.429000000000E+04 + 60 1.500000000000E-02 1.845000000000E+04 + 60 2.000000000000E-02 8.437000000000E+03 + 60 3.000000000000E-02 2.749000000000E+03 + 60 4.000000000000E-02 1.228000000000E+03 + 60 4.357000000000E-02 9.650000000000E+02 + 60 4.357000010000E-02 5.519000000000E+03 + 60 5.000000000000E-02 3.827000000000E+03 + 60 6.000000000000E-02 2.373000000000E+03 + 60 8.000000000000E-02 1.087000000000E+03 + 60 1.000000000000E-01 5.865000000000E+02 + 60 1.500000000000E-01 1.883000000000E+02 + 60 2.000000000000E-01 8.397000000000E+01 + 60 3.000000000000E-01 2.742000000000E+01 + 60 4.000000000000E-01 1.278000000000E+01 + 60 5.000000000000E-01 7.261000000000E+00 + 60 6.000000000000E-01 4.673000000000E+00 + 60 8.000000000000E-01 2.433000000000E+00 + 60 1.000000000000E+00 1.522000000000E+00 + 60 1.022000000000E+00 1.457000000000E+00 + 60 1.250000000000E+00 9.821000000000E-01 + 60 1.500000000000E+00 7.020000000000E-01 + 60 2.000000000000E+00 4.263000000000E-01 + 60 2.044000000000E+00 4.112000000000E-01 + 60 3.000000000000E+00 2.252000000000E-01 + 60 4.000000000000E+00 1.486000000000E-01 + 60 5.000000000000E+00 1.096000000000E-01 + 60 6.000000000000E+00 8.635000000000E-02 + 60 7.000000000000E+00 7.100000000000E-02 + 60 8.000000000000E+00 6.017000000000E-02 + 60 9.000000000000E+00 5.214000000000E-02 + 60 1.000000000000E+01 4.597000000000E-02 + 61 1.000000000000E-03 4.925000000000E+05 + 61 1.013000000000E-03 4.805000000000E+05 + 61 1.027000000000E-03 4.691000000000E+05 + 61 1.027000100000E-03 6.122000000000E+05 + 61 1.039000000000E-03 9.259000000000E+05 + 61 1.051000000000E-03 1.394000000000E+06 + 61 1.051000100000E-03 1.731000000000E+06 + 61 1.194000000000E-03 1.538000000000E+06 + 61 1.357000000000E-03 1.367000000000E+06 + 61 1.357000100000E-03 1.575000000000E+06 + 61 1.413000000000E-03 1.438000000000E+06 + 61 1.471000000000E-03 1.313000000000E+06 + 61 1.471000100000E-03 1.393000000000E+06 + 61 1.500000000000E-03 1.334000000000E+06 + 61 1.653000000000E-03 1.080000000000E+06 + 61 1.653000100000E-03 1.129000000000E+06 + 61 2.000000000000E-03 7.314000000000E+05 + 61 3.000000000000E-03 2.777000000000E+05 + 61 4.000000000000E-03 1.358000000000E+05 + 61 5.000000000000E-03 7.702000000000E+04 + 61 6.000000000000E-03 4.814000000000E+04 + 61 6.459000000000E-03 3.974000000000E+04 + 61 6.459000100000E-03 1.114000000000E+05 + 61 6.730000000000E-03 1.004000000000E+05 + 61 7.013000000000E-03 9.043000000000E+04 + 61 7.013000100000E-03 1.234000000000E+05 + 61 7.217000000000E-03 1.149000000000E+05 + 61 7.428000000000E-03 1.071000000000E+05 + 61 7.428000100000E-03 1.237000000000E+05 + 61 8.000000000000E-03 1.028000000000E+05 + 61 1.000000000000E-02 5.789000000000E+04 + 61 1.500000000000E-02 1.969000000000E+04 + 61 2.000000000000E-02 9.022000000000E+03 + 61 3.000000000000E-02 2.946000000000E+03 + 61 4.000000000000E-02 1.317000000000E+03 + 61 4.518000000000E-02 9.349000000000E+02 + 61 4.518000010000E-02 5.305000000000E+03 + 61 5.000000000000E-02 4.046000000000E+03 + 61 6.000000000000E-02 2.513000000000E+03 + 61 8.000000000000E-02 1.154000000000E+03 + 61 1.000000000000E-01 6.243000000000E+02 + 61 1.500000000000E-01 2.011000000000E+02 + 61 2.000000000000E-01 8.984000000000E+01 + 61 3.000000000000E-01 2.941000000000E+01 + 61 4.000000000000E-01 1.373000000000E+01 + 61 5.000000000000E-01 7.810000000000E+00 + 61 6.000000000000E-01 5.030000000000E+00 + 61 8.000000000000E-01 2.621000000000E+00 + 61 1.000000000000E+00 1.640000000000E+00 + 61 1.022000000000E+00 1.570000000000E+00 + 61 1.250000000000E+00 1.058000000000E+00 + 61 1.500000000000E+00 7.562000000000E-01 + 61 2.000000000000E+00 4.591000000000E-01 + 61 2.044000000000E+00 4.429000000000E-01 + 61 3.000000000000E+00 2.424000000000E-01 + 61 4.000000000000E+00 1.599000000000E-01 + 61 5.000000000000E+00 1.179000000000E-01 + 61 6.000000000000E+00 9.285000000000E-02 + 61 7.000000000000E+00 7.633000000000E-02 + 61 8.000000000000E+00 6.467000000000E-02 + 61 9.000000000000E+00 5.604000000000E-02 + 61 1.000000000000E+01 4.940000000000E-02 + 62 1.000000000000E-03 5.236000000000E+05 + 62 1.039000000000E-03 4.874000000000E+05 + 62 1.080000000000E-03 4.539000000000E+05 + 62 1.080000100000E-03 6.278000000000E+05 + 62 1.093000000000E-03 9.345000000000E+05 + 62 1.106000000000E-03 1.386000000000E+06 + 62 1.106000100000E-03 1.694000000000E+06 + 62 1.253000000000E-03 1.491000000000E+06 + 62 1.420000000000E-03 1.312000000000E+06 + 62 1.420000100000E-03 1.514000000000E+06 + 62 1.500000000000E-03 1.335000000000E+06 + 62 1.541000000000E-03 1.258000000000E+06 + 62 1.541000100000E-03 1.335000000000E+06 + 62 1.629000000000E-03 1.180000000000E+06 + 62 1.723000000000E-03 1.044000000000E+06 + 62 1.723000100000E-03 1.091000000000E+06 + 62 2.000000000000E-03 7.769000000000E+05 + 62 3.000000000000E-03 2.961000000000E+05 + 62 4.000000000000E-03 1.449000000000E+05 + 62 5.000000000000E-03 8.231000000000E+04 + 62 6.000000000000E-03 5.149000000000E+04 + 62 6.716000000000E-03 3.842000000000E+04 + 62 6.716000100000E-03 1.070000000000E+05 + 62 7.008000000000E-03 9.601000000000E+04 + 62 7.312000000000E-03 8.615000000000E+04 + 62 7.312000100000E-03 1.176000000000E+05 + 62 7.521000000000E-03 1.097000000000E+05 + 62 7.737000000000E-03 1.023000000000E+05 + 62 7.737000100000E-03 1.182000000000E+05 + 62 8.000000000000E-03 1.088000000000E+05 + 62 1.000000000000E-02 6.154000000000E+04 + 62 1.500000000000E-02 2.098000000000E+04 + 62 2.000000000000E-02 9.633000000000E+03 + 62 3.000000000000E-02 3.153000000000E+03 + 62 4.000000000000E-02 1.412000000000E+03 + 62 4.683000000000E-02 9.062000000000E+02 + 62 4.683000010000E-02 5.098000000000E+03 + 62 5.000000000000E-02 4.297000000000E+03 + 62 6.000000000000E-02 2.657000000000E+03 + 62 8.000000000000E-02 1.224000000000E+03 + 62 1.000000000000E-01 6.636000000000E+02 + 62 1.500000000000E-01 2.145000000000E+02 + 62 2.000000000000E-01 9.601000000000E+01 + 62 3.000000000000E-01 3.151000000000E+01 + 62 4.000000000000E-01 1.474000000000E+01 + 62 5.000000000000E-01 8.390000000000E+00 + 62 6.000000000000E-01 5.407000000000E+00 + 62 8.000000000000E-01 2.820000000000E+00 + 62 1.000000000000E+00 1.765000000000E+00 + 62 1.022000000000E+00 1.689000000000E+00 + 62 1.250000000000E+00 1.139000000000E+00 + 62 1.500000000000E+00 8.136000000000E-01 + 62 2.000000000000E+00 4.939000000000E-01 + 62 2.044000000000E+00 4.763000000000E-01 + 62 3.000000000000E+00 2.605000000000E-01 + 62 4.000000000000E+00 1.719000000000E-01 + 62 5.000000000000E+00 1.267000000000E-01 + 62 6.000000000000E+00 9.972000000000E-02 + 62 7.000000000000E+00 8.196000000000E-02 + 62 8.000000000000E+00 6.943000000000E-02 + 62 9.000000000000E+00 6.015000000000E-02 + 62 1.000000000000E+01 5.302000000000E-02 + 63 1.000000000000E-03 5.566000000000E+05 + 63 1.063000000000E-03 4.962000000000E+05 + 63 1.131000000000E-03 4.424000000000E+05 + 63 1.131000100000E-03 5.878000000000E+05 + 63 1.146000000000E-03 8.902000000000E+05 + 63 1.161000000000E-03 1.349000000000E+06 + 63 1.161000100000E-03 1.632000000000E+06 + 63 1.311000000000E-03 1.438000000000E+06 + 63 1.481000000000E-03 1.267000000000E+06 + 63 1.481000100000E-03 1.464000000000E+06 + 63 1.500000000000E-03 1.417000000000E+06 + 63 1.614000000000E-03 1.202000000000E+06 + 63 1.614000100000E-03 1.276000000000E+06 + 63 1.704000000000E-03 1.131000000000E+06 + 63 1.800000000000E-03 1.002000000000E+06 + 63 1.800000100000E-03 1.047000000000E+06 + 63 2.000000000000E-03 8.250000000000E+05 + 63 3.000000000000E-03 3.150000000000E+05 + 63 4.000000000000E-03 1.545000000000E+05 + 63 5.000000000000E-03 8.783000000000E+04 + 63 6.000000000000E-03 5.499000000000E+04 + 63 6.977000000000E-03 3.717000000000E+04 + 63 6.977000100000E-03 1.029000000000E+05 + 63 7.290000000000E-03 9.196000000000E+04 + 63 7.617000000000E-03 8.218000000000E+04 + 63 7.617000100000E-03 1.122000000000E+05 + 63 8.000000000000E-03 9.957000000000E+04 + 63 8.052000000000E-03 9.785000000000E+04 + 63 8.052000100000E-03 1.131000000000E+05 + 63 1.000000000000E-02 6.545000000000E+04 + 63 1.500000000000E-02 2.234000000000E+04 + 63 2.000000000000E-02 1.027000000000E+04 + 63 3.000000000000E-02 3.370000000000E+03 + 63 4.000000000000E-02 1.512000000000E+03 + 63 4.852000000000E-02 8.788000000000E+02 + 63 4.852000010000E-02 4.906000000000E+03 + 63 5.000000000000E-02 4.530000000000E+03 + 63 6.000000000000E-02 2.804000000000E+03 + 63 8.000000000000E-02 1.297000000000E+03 + 63 1.000000000000E-01 7.045000000000E+02 + 63 1.500000000000E-01 2.285000000000E+02 + 63 2.000000000000E-01 1.025000000000E+02 + 63 3.000000000000E-01 3.372000000000E+01 + 63 4.000000000000E-01 1.579000000000E+01 + 63 5.000000000000E-01 9.001000000000E+00 + 63 6.000000000000E-01 5.805000000000E+00 + 63 8.000000000000E-01 3.030000000000E+00 + 63 1.000000000000E+00 1.897000000000E+00 + 63 1.022000000000E+00 1.816000000000E+00 + 63 1.250000000000E+00 1.224000000000E+00 + 63 1.500000000000E+00 8.742000000000E-01 + 63 2.000000000000E+00 5.306000000000E-01 + 63 2.044000000000E+00 5.117000000000E-01 + 63 3.000000000000E+00 2.798000000000E-01 + 63 4.000000000000E+00 1.845000000000E-01 + 63 5.000000000000E+00 1.359000000000E-01 + 63 6.000000000000E+00 1.070000000000E-01 + 63 7.000000000000E+00 8.790000000000E-02 + 63 8.000000000000E+00 7.445000000000E-02 + 63 9.000000000000E+00 6.449000000000E-02 + 63 1.000000000000E+01 5.684000000000E-02 + 64 1.000000000000E-03 5.957000000000E+05 + 64 1.089000000000E-03 5.080000000000E+05 + 64 1.185000000000E-03 4.333000000000E+05 + 64 1.185000100000E-03 4.789000000000E+05 + 64 1.201000000000E-03 7.071000000000E+05 + 64 1.217000000000E-03 1.041000000000E+06 + 64 1.217000100000E-03 1.255000000000E+06 + 64 1.500000000000E-03 1.314000000000E+06 + 64 1.544000000000E-03 1.225000000000E+06 + 64 1.544000100000E-03 1.416000000000E+06 + 64 1.615000000000E-03 1.277000000000E+06 + 64 1.688000000000E-03 1.152000000000E+06 + 64 1.688000100000E-03 1.223000000000E+06 + 64 1.782000000000E-03 1.085000000000E+06 + 64 1.881000000000E-03 9.616000000000E+05 + 64 1.881000100000E-03 1.004000000000E+06 + 64 2.000000000000E-03 8.750000000000E+05 + 64 3.000000000000E-03 3.353000000000E+05 + 64 4.000000000000E-03 1.648000000000E+05 + 64 5.000000000000E-03 9.379000000000E+04 + 64 6.000000000000E-03 5.877000000000E+04 + 64 7.243000000000E-03 3.606000000000E+04 + 64 7.243000100000E-03 9.913000000000E+04 + 64 7.579000000000E-03 8.820000000000E+04 + 64 7.930000000000E-03 7.847000000000E+04 + 64 7.930000100000E-03 1.071000000000E+05 + 64 8.000000000000E-03 1.051000000000E+05 + 64 8.376000000000E-03 9.372000000000E+04 + 64 8.376000100000E-03 1.083000000000E+05 + 64 1.000000000000E-02 6.938000000000E+04 + 64 1.500000000000E-02 2.376000000000E+04 + 64 2.000000000000E-02 1.094000000000E+04 + 64 3.000000000000E-02 3.599000000000E+03 + 64 4.000000000000E-02 1.617000000000E+03 + 64 5.000000000000E-02 8.647000000000E+02 + 64 5.024000000000E-02 8.531000000000E+02 + 64 5.024000010000E-02 4.724000000000E+03 + 64 6.000000000000E-02 2.954000000000E+03 + 64 8.000000000000E-02 1.373000000000E+03 + 64 1.000000000000E-01 7.473000000000E+02 + 64 1.500000000000E-01 2.431000000000E+02 + 64 2.000000000000E-01 1.093000000000E+02 + 64 3.000000000000E-01 3.604000000000E+01 + 64 4.000000000000E-01 1.691000000000E+01 + 64 5.000000000000E-01 9.647000000000E+00 + 64 6.000000000000E-01 6.226000000000E+00 + 64 8.000000000000E-01 3.252000000000E+00 + 64 1.000000000000E+00 2.037000000000E+00 + 64 1.022000000000E+00 1.949000000000E+00 + 64 1.250000000000E+00 1.314000000000E+00 + 64 1.500000000000E+00 9.385000000000E-01 + 64 2.000000000000E+00 5.694000000000E-01 + 64 2.044000000000E+00 5.492000000000E-01 + 64 3.000000000000E+00 3.001000000000E-01 + 64 4.000000000000E+00 1.978000000000E-01 + 64 5.000000000000E+00 1.457000000000E-01 + 64 6.000000000000E+00 1.146000000000E-01 + 64 7.000000000000E+00 9.417000000000E-02 + 64 8.000000000000E+00 7.975000000000E-02 + 64 9.000000000000E+00 6.908000000000E-02 + 64 1.000000000000E+01 6.087000000000E-02 + 65 1.000000000000E-03 6.297000000000E+05 + 65 1.114000000000E-03 5.126000000000E+05 + 65 1.241000000000E-03 4.174000000000E+05 + 65 1.241000100000E-03 6.006000000000E+05 + 65 1.258000000000E-03 8.868000000000E+05 + 65 1.275000000000E-03 1.309000000000E+06 + 65 1.275000100000E-03 1.549000000000E+06 + 65 1.500000000000E-03 1.400000000000E+06 + 65 1.611000000000E-03 1.176000000000E+06 + 65 1.611000100000E-03 1.360000000000E+06 + 65 1.688000000000E-03 1.222000000000E+06 + 65 1.768000000000E-03 1.097000000000E+06 + 65 1.768000100000E-03 1.165000000000E+06 + 65 1.865000000000E-03 1.033000000000E+06 + 65 1.968000000000E-03 9.164000000000E+05 + 65 1.968000100000E-03 9.574000000000E+05 + 65 2.000000000000E-03 9.232000000000E+05 + 65 3.000000000000E-03 3.553000000000E+05 + 65 4.000000000000E-03 1.748000000000E+05 + 65 5.000000000000E-03 9.960000000000E+04 + 65 6.000000000000E-03 6.246000000000E+04 + 65 7.514000000000E-03 3.485000000000E+04 + 65 7.514000100000E-03 9.527000000000E+04 + 65 8.000000000000E-03 8.148000000000E+04 + 65 8.252000000000E-03 7.488000000000E+04 + 65 8.252000100000E-03 1.024000000000E+05 + 65 8.477000000000E-03 9.584000000000E+04 + 65 8.708000000000E-03 8.969000000000E+04 + 65 8.708000100000E-03 1.037000000000E+05 + 65 1.000000000000E-02 7.331000000000E+04 + 65 1.500000000000E-02 2.522000000000E+04 + 65 2.000000000000E-02 1.164000000000E+04 + 65 3.000000000000E-02 3.837000000000E+03 + 65 4.000000000000E-02 1.726000000000E+03 + 65 5.000000000000E-02 9.240000000000E+02 + 65 5.200000000000E-02 8.278000000000E+02 + 65 5.200000010000E-02 4.547000000000E+03 + 65 6.000000000000E-02 3.109000000000E+03 + 65 8.000000000000E-02 1.452000000000E+03 + 65 1.000000000000E-01 7.918000000000E+02 + 65 1.500000000000E-01 2.583000000000E+02 + 65 2.000000000000E-01 1.163000000000E+02 + 65 3.000000000000E-01 3.847000000000E+01 + 65 4.000000000000E-01 1.808000000000E+01 + 65 5.000000000000E-01 1.033000000000E+01 + 65 6.000000000000E-01 6.669000000000E+00 + 65 8.000000000000E-01 3.486000000000E+00 + 65 1.000000000000E+00 2.184000000000E+00 + 65 1.022000000000E+00 2.090000000000E+00 + 65 1.250000000000E+00 1.409000000000E+00 + 65 1.500000000000E+00 1.006000000000E+00 + 65 2.000000000000E+00 6.103000000000E-01 + 65 2.044000000000E+00 5.886000000000E-01 + 65 3.000000000000E+00 3.215000000000E-01 + 65 4.000000000000E+00 2.118000000000E-01 + 65 5.000000000000E+00 1.560000000000E-01 + 65 6.000000000000E+00 1.227000000000E-01 + 65 7.000000000000E+00 1.008000000000E-01 + 65 8.000000000000E+00 8.533000000000E-02 + 65 9.000000000000E+00 7.389000000000E-02 + 65 1.000000000000E+01 6.511000000000E-02 + 66 1.000000000000E-03 6.702000000000E+05 + 66 1.138000000000E-03 5.228000000000E+05 + 66 1.295000000000E-03 4.079000000000E+05 + 66 1.295000100000E-03 5.703000000000E+05 + 66 1.314000000000E-03 8.489000000000E+05 + 66 1.332000000000E-03 1.264000000000E+06 + 66 1.332000100000E-03 1.483000000000E+06 + 66 1.500000000000E-03 1.495000000000E+06 + 66 1.676000000000E-03 1.139000000000E+06 + 66 1.676000100000E-03 1.318000000000E+06 + 66 1.757000000000E-03 1.180000000000E+06 + 66 1.842000000000E-03 1.056000000000E+06 + 66 1.842000100000E-03 1.122000000000E+06 + 66 2.000000000000E-03 9.329000000000E+05 + 66 2.047000000000E-03 8.847000000000E+05 + 66 2.047000100000E-03 9.241000000000E+05 + 66 3.000000000000E-03 3.768000000000E+05 + 66 4.000000000000E-03 1.856000000000E+05 + 66 5.000000000000E-03 1.059000000000E+05 + 66 6.000000000000E-03 6.644000000000E+04 + 66 7.790000000000E-03 3.377000000000E+04 + 66 7.790000100000E-03 9.146000000000E+04 + 66 8.000000000000E-03 8.696000000000E+04 + 66 8.581000000000E-03 7.154000000000E+04 + 66 8.581000100000E-03 9.793000000000E+04 + 66 8.810000000000E-03 9.177000000000E+04 + 66 9.046000000000E-03 8.600000000000E+04 + 66 9.046000100000E-03 9.940000000000E+04 + 66 1.000000000000E-02 7.729000000000E+04 + 66 1.500000000000E-02 2.676000000000E+04 + 66 2.000000000000E-02 1.237000000000E+04 + 66 3.000000000000E-02 4.088000000000E+03 + 66 4.000000000000E-02 1.841000000000E+03 + 66 5.000000000000E-02 9.868000000000E+02 + 66 5.379000000000E-02 8.039000000000E+02 + 66 5.379000010000E-02 4.382000000000E+03 + 66 6.000000000000E-02 3.274000000000E+03 + 66 8.000000000000E-02 1.535000000000E+03 + 66 1.000000000000E-01 8.380000000000E+02 + 66 1.500000000000E-01 2.742000000000E+02 + 66 2.000000000000E-01 1.237000000000E+02 + 66 3.000000000000E-01 4.103000000000E+01 + 66 4.000000000000E-01 1.931000000000E+01 + 66 5.000000000000E-01 1.104000000000E+01 + 66 6.000000000000E-01 7.136000000000E+00 + 66 8.000000000000E-01 3.733000000000E+00 + 66 1.000000000000E+00 2.340000000000E+00 + 66 1.022000000000E+00 2.239000000000E+00 + 66 1.250000000000E+00 1.509000000000E+00 + 66 1.500000000000E+00 1.078000000000E+00 + 66 2.000000000000E+00 6.536000000000E-01 + 66 2.044000000000E+00 6.303000000000E-01 + 66 3.000000000000E+00 3.441000000000E-01 + 66 4.000000000000E+00 2.266000000000E-01 + 66 5.000000000000E+00 1.668000000000E-01 + 66 6.000000000000E+00 1.312000000000E-01 + 66 7.000000000000E+00 1.077000000000E-01 + 66 8.000000000000E+00 9.121000000000E-02 + 66 9.000000000000E+00 7.898000000000E-02 + 66 1.000000000000E+01 6.959000000000E-02 + 67 1.000000000000E-03 7.136000000000E+05 + 67 1.162000000000E-03 5.328000000000E+05 + 67 1.351000000000E-03 3.979000000000E+05 + 67 1.351000100000E-03 5.637000000000E+05 + 67 1.371000000000E-03 8.318000000000E+05 + 67 1.391000000000E-03 1.226000000000E+06 + 67 1.391000100000E-03 1.419000000000E+06 + 67 1.500000000000E-03 1.599000000000E+06 + 67 1.741000000000E-03 1.103000000000E+06 + 67 1.741000100000E-03 1.278000000000E+06 + 67 1.830000000000E-03 1.137000000000E+06 + 67 1.923000000000E-03 1.012000000000E+06 + 67 1.923000100000E-03 1.075000000000E+06 + 67 2.000000000000E-03 9.808000000000E+05 + 67 2.128000000000E-03 8.542000000000E+05 + 67 2.128000100000E-03 8.920000000000E+05 + 67 3.000000000000E-03 3.988000000000E+05 + 67 4.000000000000E-03 1.969000000000E+05 + 67 5.000000000000E-03 1.124000000000E+05 + 67 6.000000000000E-03 7.058000000000E+04 + 67 8.000000000000E-03 3.350000000000E+04 + 67 8.071000000000E-03 3.274000000000E+04 + 67 8.071000100000E-03 8.864000000000E+04 + 67 8.484000000000E-03 7.786000000000E+04 + 67 8.918000000000E-03 6.839000000000E+04 + 67 8.918000100000E-03 9.368000000000E+04 + 67 9.153000000000E-03 8.787000000000E+04 + 67 9.394000000000E-03 8.244000000000E+04 + 67 9.394000100000E-03 9.529000000000E+04 + 67 1.000000000000E-02 8.143000000000E+04 + 67 1.500000000000E-02 2.835000000000E+04 + 67 2.000000000000E-02 1.314000000000E+04 + 67 3.000000000000E-02 4.350000000000E+03 + 67 4.000000000000E-02 1.962000000000E+03 + 67 5.000000000000E-02 1.053000000000E+03 + 67 5.562000000000E-02 7.811000000000E+02 + 67 5.562000010000E-02 4.221000000000E+03 + 67 6.000000000000E-02 3.459000000000E+03 + 67 8.000000000000E-02 1.619000000000E+03 + 67 1.000000000000E-01 8.858000000000E+02 + 67 1.500000000000E-01 2.908000000000E+02 + 67 2.000000000000E-01 1.314000000000E+02 + 67 3.000000000000E-01 4.370000000000E+01 + 67 4.000000000000E-01 2.060000000000E+01 + 67 5.000000000000E-01 1.179000000000E+01 + 67 6.000000000000E-01 7.627000000000E+00 + 67 8.000000000000E-01 3.994000000000E+00 + 67 1.000000000000E+00 2.504000000000E+00 + 67 1.022000000000E+00 2.396000000000E+00 + 67 1.250000000000E+00 1.615000000000E+00 + 67 1.500000000000E+00 1.153000000000E+00 + 67 2.000000000000E+00 6.991000000000E-01 + 67 2.044000000000E+00 6.742000000000E-01 + 67 3.000000000000E+00 3.679000000000E-01 + 67 4.000000000000E+00 2.422000000000E-01 + 67 5.000000000000E+00 1.782000000000E-01 + 67 6.000000000000E+00 1.401000000000E-01 + 67 7.000000000000E+00 1.151000000000E-01 + 67 8.000000000000E+00 9.739000000000E-02 + 67 9.000000000000E+00 8.432000000000E-02 + 67 1.000000000000E+01 7.428000000000E-02 + 68 1.000000000000E-03 7.602000000000E+05 + 68 1.187000000000E-03 5.433000000000E+05 + 68 1.409000000000E-03 3.882000000000E+05 + 68 1.409000100000E-03 5.636000000000E+05 + 68 1.431000000000E-03 8.231000000000E+05 + 68 1.453000000000E-03 1.200000000000E+06 + 68 1.453000100000E-03 1.350000000000E+06 + 68 1.500000000000E-03 1.683000000000E+06 + 68 1.812000000000E-03 1.064000000000E+06 + 68 1.812000100000E-03 1.234000000000E+06 + 68 2.000000000000E-03 9.758000000000E+05 + 68 2.006000000000E-03 9.692000000000E+05 + 68 2.006000100000E-03 1.030000000000E+06 + 68 2.104000000000E-03 9.241000000000E+05 + 68 2.206000000000E-03 8.293000000000E+05 + 68 2.206000100000E-03 8.658000000000E+05 + 68 3.000000000000E-03 4.215000000000E+05 + 68 4.000000000000E-03 2.086000000000E+05 + 68 5.000000000000E-03 1.192000000000E+05 + 68 6.000000000000E-03 7.489000000000E+04 + 68 8.000000000000E-03 3.559000000000E+04 + 68 8.358000000000E-03 3.175000000000E+04 + 68 8.358000100000E-03 8.551000000000E+04 + 68 8.799000000000E-03 7.478000000000E+04 + 68 9.264000000000E-03 6.540000000000E+04 + 68 9.264000100000E-03 8.966000000000E+04 + 68 9.505000000000E-03 8.418000000000E+04 + 68 9.751000000000E-03 7.903000000000E+04 + 68 9.751000100000E-03 9.135000000000E+04 + 68 1.000000000000E-02 8.580000000000E+04 + 68 1.500000000000E-02 3.000000000000E+04 + 68 2.000000000000E-02 1.393000000000E+04 + 68 3.000000000000E-02 4.623000000000E+03 + 68 4.000000000000E-02 2.089000000000E+03 + 68 5.000000000000E-02 1.122000000000E+03 + 68 5.749000000000E-02 7.593000000000E+02 + 68 5.749000010000E-02 4.065000000000E+03 + 68 6.000000000000E-02 3.651000000000E+03 + 68 8.000000000000E-02 1.706000000000E+03 + 68 1.000000000000E-01 9.348000000000E+02 + 68 1.500000000000E-01 3.080000000000E+02 + 68 2.000000000000E-01 1.395000000000E+02 + 68 3.000000000000E-01 4.650000000000E+01 + 68 4.000000000000E-01 2.195000000000E+01 + 68 5.000000000000E-01 1.258000000000E+01 + 68 6.000000000000E-01 8.144000000000E+00 + 68 8.000000000000E-01 4.268000000000E+00 + 68 1.000000000000E+00 2.677000000000E+00 + 68 1.022000000000E+00 2.562000000000E+00 + 68 1.250000000000E+00 1.727000000000E+00 + 68 1.500000000000E+00 1.232000000000E+00 + 68 2.000000000000E+00 7.471000000000E-01 + 68 2.044000000000E+00 7.205000000000E-01 + 68 3.000000000000E+00 3.929000000000E-01 + 68 4.000000000000E+00 2.586000000000E-01 + 68 5.000000000000E+00 1.902000000000E-01 + 68 6.000000000000E+00 1.495000000000E-01 + 68 7.000000000000E+00 1.228000000000E-01 + 68 8.000000000000E+00 1.039000000000E-01 + 68 9.000000000000E+00 8.994000000000E-02 + 68 1.000000000000E+01 7.922000000000E-02 + 69 1.000000000000E-03 8.102000000000E+05 + 69 1.211000000000E-03 5.543000000000E+05 + 69 1.468000000000E-03 3.793000000000E+05 + 69 1.468000100000E-03 5.175000000000E+05 + 69 1.500000000000E-03 1.101000000000E+06 + 69 1.515000000000E-03 1.160000000000E+06 + 69 1.515000100000E-03 1.352000000000E+06 + 69 1.689000000000E-03 1.178000000000E+06 + 69 1.884000000000E-03 1.026000000000E+06 + 69 1.884000100000E-03 1.190000000000E+06 + 69 2.000000000000E-03 1.031000000000E+06 + 69 2.090000000000E-03 9.304000000000E+05 + 69 2.090000100000E-03 9.886000000000E+05 + 69 2.196000000000E-03 8.832000000000E+05 + 69 2.307000000000E-03 7.890000000000E+05 + 69 2.307000100000E-03 8.237000000000E+05 + 69 3.000000000000E-03 4.446000000000E+05 + 69 4.000000000000E-03 2.207000000000E+05 + 69 5.000000000000E-03 1.262000000000E+05 + 69 6.000000000000E-03 7.937000000000E+04 + 69 8.000000000000E-03 3.777000000000E+04 + 69 8.648000000000E-03 3.082000000000E+04 + 69 8.648000100000E-03 8.260000000000E+04 + 69 9.120000000000E-03 7.191000000000E+04 + 69 9.617000000000E-03 6.260000000000E+04 + 69 9.617000100000E-03 8.581000000000E+04 + 69 1.000000000000E-02 7.825000000000E+04 + 69 1.012000000000E-02 7.586000000000E+04 + 69 1.012000010000E-02 8.770000000000E+04 + 69 1.500000000000E-02 3.170000000000E+04 + 69 2.000000000000E-02 1.475000000000E+04 + 69 3.000000000000E-02 4.909000000000E+03 + 69 4.000000000000E-02 2.222000000000E+03 + 69 5.000000000000E-02 1.195000000000E+03 + 69 5.939000000000E-02 7.383000000000E+02 + 69 5.939000010000E-02 3.964000000000E+03 + 69 6.000000000000E-02 3.816000000000E+03 + 69 8.000000000000E-02 1.795000000000E+03 + 69 1.000000000000E-01 9.854000000000E+02 + 69 1.500000000000E-01 3.258000000000E+02 + 69 2.000000000000E-01 1.479000000000E+02 + 69 3.000000000000E-01 4.943000000000E+01 + 69 4.000000000000E-01 2.337000000000E+01 + 69 5.000000000000E-01 1.341000000000E+01 + 69 6.000000000000E-01 8.688000000000E+00 + 69 8.000000000000E-01 4.557000000000E+00 + 69 1.000000000000E+00 2.858000000000E+00 + 69 1.022000000000E+00 2.736000000000E+00 + 69 1.250000000000E+00 1.844000000000E+00 + 69 1.500000000000E+00 1.316000000000E+00 + 69 2.000000000000E+00 7.976000000000E-01 + 69 2.044000000000E+00 7.692000000000E-01 + 69 3.000000000000E+00 4.193000000000E-01 + 69 4.000000000000E+00 2.758000000000E-01 + 69 5.000000000000E+00 2.029000000000E-01 + 69 6.000000000000E+00 1.594000000000E-01 + 69 7.000000000000E+00 1.308000000000E-01 + 69 8.000000000000E+00 1.107000000000E-01 + 69 9.000000000000E+00 9.584000000000E-02 + 69 1.000000000000E+01 8.441000000000E-02 + 70 1.000000000000E-03 8.639000000000E+05 + 70 1.500000000000E-03 3.850000000000E+05 + 70 1.528000000000E-03 3.706000000000E+05 + 70 1.528000100000E-03 5.567000000000E+05 + 70 1.552000000000E-03 7.889000000000E+05 + 70 1.576000000000E-03 1.119000000000E+06 + 70 1.576000100000E-03 1.301000000000E+06 + 70 1.753000000000E-03 1.141000000000E+06 + 70 1.950000000000E-03 1.002000000000E+06 + 70 1.950000100000E-03 1.162000000000E+06 + 70 2.000000000000E-03 1.088000000000E+06 + 70 2.173000000000E-03 8.961000000000E+05 + 70 2.173000100000E-03 9.522000000000E+05 + 70 2.283000000000E-03 8.505000000000E+05 + 70 2.398000000000E-03 7.596000000000E+05 + 70 2.398000100000E-03 7.930000000000E+05 + 70 3.000000000000E-03 4.688000000000E+05 + 70 4.000000000000E-03 2.331000000000E+05 + 70 5.000000000000E-03 1.335000000000E+05 + 70 6.000000000000E-03 8.403000000000E+04 + 70 8.000000000000E-03 4.003000000000E+04 + 70 8.944000000000E-03 2.993000000000E+04 + 70 8.944000100000E-03 7.982000000000E+04 + 70 9.447000000000E-03 6.917000000000E+04 + 70 9.978000000000E-03 5.996000000000E+04 + 70 9.978000100000E-03 8.442000000000E+04 + 70 1.000000000000E-02 8.338000000000E+04 + 70 1.049000000000E-02 7.289000000000E+04 + 70 1.049000010000E-02 8.427000000000E+04 + 70 1.500000000000E-02 3.349000000000E+04 + 70 2.000000000000E-02 1.561000000000E+04 + 70 3.000000000000E-02 5.208000000000E+03 + 70 4.000000000000E-02 2.361000000000E+03 + 70 5.000000000000E-02 1.271000000000E+03 + 70 6.000000000000E-02 7.639000000000E+02 + 70 6.133000000000E-02 7.183000000000E+02 + 70 6.133000010000E-02 3.786000000000E+03 + 70 8.000000000000E-02 1.886000000000E+03 + 70 1.000000000000E-01 1.038000000000E+03 + 70 1.500000000000E-01 3.444000000000E+02 + 70 2.000000000000E-01 1.567000000000E+02 + 70 3.000000000000E-01 5.248000000000E+01 + 70 4.000000000000E-01 2.486000000000E+01 + 70 5.000000000000E-01 1.428000000000E+01 + 70 6.000000000000E-01 9.258000000000E+00 + 70 8.000000000000E-01 4.860000000000E+00 + 70 1.000000000000E+00 3.050000000000E+00 + 70 1.022000000000E+00 2.919000000000E+00 + 70 1.250000000000E+00 1.968000000000E+00 + 70 1.500000000000E+00 1.404000000000E+00 + 70 2.000000000000E+00 8.507000000000E-01 + 70 2.044000000000E+00 8.204000000000E-01 + 70 3.000000000000E+00 4.470000000000E-01 + 70 4.000000000000E+00 2.939000000000E-01 + 70 5.000000000000E+00 2.161000000000E-01 + 70 6.000000000000E+00 1.698000000000E-01 + 70 7.000000000000E+00 1.393000000000E-01 + 70 8.000000000000E+00 1.179000000000E-01 + 70 9.000000000000E+00 1.020000000000E-01 + 70 1.000000000000E+01 8.986000000000E-02 + 71 1.000000000000E-03 9.227000000000E+05 + 71 1.500000000000E-03 4.107000000000E+05 + 71 1.588000000000E-03 3.643000000000E+05 + 71 1.588000100000E-03 4.530000000000E+05 + 71 1.614000000000E-03 6.693000000000E+05 + 71 1.639000000000E-03 9.865000000000E+05 + 71 1.639000100000E-03 1.131000000000E+06 + 71 2.000000000000E-03 1.000000000000E+06 + 71 2.024000000000E-03 9.710000000000E+05 + 71 2.024000100000E-03 1.127000000000E+06 + 71 2.140000000000E-03 9.844000000000E+05 + 71 2.263000000000E-03 8.599000000000E+05 + 71 2.263000100000E-03 9.139000000000E+05 + 71 2.375000000000E-03 8.181000000000E+05 + 71 2.491000000000E-03 7.326000000000E+05 + 71 2.491000100000E-03 7.644000000000E+05 + 71 3.000000000000E-03 4.942000000000E+05 + 71 4.000000000000E-03 2.463000000000E+05 + 71 5.000000000000E-03 1.413000000000E+05 + 71 6.000000000000E-03 8.902000000000E+04 + 71 8.000000000000E-03 4.246000000000E+04 + 71 9.244000000000E-03 2.913000000000E+04 + 71 9.244000100000E-03 7.720000000000E+04 + 71 1.000000000000E-02 6.299000000000E+04 + 71 1.035000000000E-02 5.746000000000E+04 + 71 1.035000010000E-02 7.892000000000E+04 + 71 1.061000000000E-02 7.433000000000E+04 + 71 1.087000000000E-02 7.002000000000E+04 + 71 1.087000010000E-02 8.095000000000E+04 + 71 1.500000000000E-02 3.541000000000E+04 + 71 2.000000000000E-02 1.651000000000E+04 + 71 3.000000000000E-02 5.522000000000E+03 + 71 4.000000000000E-02 2.507000000000E+03 + 71 5.000000000000E-02 1.351000000000E+03 + 71 6.000000000000E-02 8.128000000000E+02 + 71 6.331000000000E-02 6.994000000000E+02 + 71 6.331000010000E-02 3.657000000000E+03 + 71 8.000000000000E-02 1.978000000000E+03 + 71 1.000000000000E-01 1.092000000000E+03 + 71 1.500000000000E-01 3.637000000000E+02 + 71 2.000000000000E-01 1.658000000000E+02 + 71 3.000000000000E-01 5.568000000000E+01 + 71 4.000000000000E-01 2.642000000000E+01 + 71 5.000000000000E-01 1.519000000000E+01 + 71 6.000000000000E-01 9.858000000000E+00 + 71 8.000000000000E-01 5.179000000000E+00 + 71 1.000000000000E+00 3.251000000000E+00 + 71 1.022000000000E+00 3.111000000000E+00 + 71 1.250000000000E+00 2.098000000000E+00 + 71 1.500000000000E+00 1.496000000000E+00 + 71 2.000000000000E+00 9.065000000000E-01 + 71 2.044000000000E+00 8.742000000000E-01 + 71 3.000000000000E+00 4.761000000000E-01 + 71 4.000000000000E+00 3.129000000000E-01 + 71 5.000000000000E+00 2.300000000000E-01 + 71 6.000000000000E+00 1.807000000000E-01 + 71 7.000000000000E+00 1.483000000000E-01 + 71 8.000000000000E+00 1.254000000000E-01 + 71 9.000000000000E+00 1.085000000000E-01 + 71 1.000000000000E+01 9.557000000000E-02 + 72 1.000000000000E-03 9.853000000000E+05 + 72 1.500000000000E-03 4.382000000000E+05 + 72 1.662000000000E-03 3.534000000000E+05 + 72 1.662000100000E-03 4.376000000000E+05 + 72 1.689000000000E-03 6.470000000000E+05 + 72 1.716000000000E-03 9.551000000000E+05 + 72 1.716000100000E-03 1.077000000000E+06 + 72 2.000000000000E-03 1.063000000000E+06 + 72 2.108000000000E-03 9.315000000000E+05 + 72 2.108000100000E-03 1.081000000000E+06 + 72 2.233000000000E-03 9.404000000000E+05 + 72 2.365000000000E-03 8.179000000000E+05 + 72 2.365000100000E-03 8.694000000000E+05 + 72 2.480000000000E-03 7.788000000000E+05 + 72 2.601000000000E-03 6.976000000000E+05 + 72 2.601000100000E-03 7.279000000000E+05 + 72 3.000000000000E-03 5.214000000000E+05 + 72 4.000000000000E-03 2.601000000000E+05 + 72 5.000000000000E-03 1.494000000000E+05 + 72 6.000000000000E-03 9.422000000000E+04 + 72 8.000000000000E-03 4.499000000000E+04 + 72 9.561000000000E-03 2.829000000000E+04 + 72 9.561000100000E-03 7.444000000000E+04 + 72 1.000000000000E-02 6.692000000000E+04 + 72 1.074000000000E-02 5.495000000000E+04 + 72 1.074000010000E-02 7.554000000000E+04 + 72 1.100000000000E-02 7.125000000000E+04 + 72 1.127000000000E-02 6.719000000000E+04 + 72 1.127000010000E-02 7.768000000000E+04 + 72 1.500000000000E-02 3.738000000000E+04 + 72 2.000000000000E-02 1.744000000000E+04 + 72 3.000000000000E-02 5.850000000000E+03 + 72 4.000000000000E-02 2.659000000000E+03 + 72 5.000000000000E-02 1.435000000000E+03 + 72 6.000000000000E-02 8.641000000000E+02 + 72 6.535000000000E-02 6.808000000000E+02 + 72 6.535000010000E-02 3.530000000000E+03 + 72 8.000000000000E-02 2.073000000000E+03 + 72 1.000000000000E-01 1.149000000000E+03 + 72 1.500000000000E-01 3.837000000000E+02 + 72 2.000000000000E-01 1.752000000000E+02 + 72 3.000000000000E-01 5.902000000000E+01 + 72 4.000000000000E-01 2.805000000000E+01 + 72 5.000000000000E-01 1.615000000000E+01 + 72 6.000000000000E-01 1.049000000000E+01 + 72 8.000000000000E-01 5.514000000000E+00 + 72 1.000000000000E+00 3.463000000000E+00 + 72 1.022000000000E+00 3.314000000000E+00 + 72 1.250000000000E+00 2.234000000000E+00 + 72 1.500000000000E+00 1.593000000000E+00 + 72 2.000000000000E+00 9.652000000000E-01 + 72 2.044000000000E+00 9.308000000000E-01 + 72 3.000000000000E+00 5.067000000000E-01 + 72 4.000000000000E+00 3.329000000000E-01 + 72 5.000000000000E+00 2.447000000000E-01 + 72 6.000000000000E+00 1.921000000000E-01 + 72 7.000000000000E+00 1.576000000000E-01 + 72 8.000000000000E+00 1.333000000000E-01 + 72 9.000000000000E+00 1.154000000000E-01 + 72 1.000000000000E+01 1.016000000000E-01 + 73 1.000000000000E-03 1.051000000000E+06 + 73 1.500000000000E-03 4.674000000000E+05 + 73 1.735000000000E-03 3.437000000000E+05 + 73 1.735000100000E-03 4.185000000000E+05 + 73 1.764000000000E-03 6.180000000000E+05 + 73 1.793000000000E-03 9.116000000000E+05 + 73 1.793000100000E-03 1.013000000000E+06 + 73 2.000000000000E-03 1.130000000000E+06 + 73 2.194000000000E-03 8.939000000000E+05 + 73 2.194000100000E-03 1.038000000000E+06 + 73 2.327000000000E-03 8.993000000000E+05 + 73 2.469000000000E-03 7.796000000000E+05 + 73 2.469000100000E-03 8.286000000000E+05 + 73 2.586000000000E-03 7.440000000000E+05 + 73 2.708000000000E-03 6.681000000000E+05 + 73 2.708000100000E-03 6.970000000000E+05 + 73 3.000000000000E-03 5.495000000000E+05 + 73 4.000000000000E-03 2.746000000000E+05 + 73 5.000000000000E-03 1.579000000000E+05 + 73 6.000000000000E-03 9.963000000000E+04 + 73 8.000000000000E-03 4.763000000000E+04 + 73 9.881000000000E-03 2.750000000000E+04 + 73 9.881000100000E-03 7.189000000000E+04 + 73 1.000000000000E-02 7.015000000000E+04 + 73 1.114000000000E-02 5.260000000000E+04 + 73 1.114000010000E-02 7.239000000000E+04 + 73 1.141000000000E-02 6.830000000000E+04 + 73 1.168000000000E-02 6.444000000000E+04 + 73 1.168000010000E-02 7.450000000000E+04 + 73 1.500000000000E-02 3.940000000000E+04 + 73 2.000000000000E-02 1.841000000000E+04 + 73 3.000000000000E-02 6.191000000000E+03 + 73 4.000000000000E-02 2.819000000000E+03 + 73 5.000000000000E-02 1.523000000000E+03 + 73 6.000000000000E-02 9.179000000000E+02 + 73 6.742000000000E-02 6.634000000000E+02 + 73 6.742000010000E-02 3.412000000000E+03 + 73 8.000000000000E-02 2.171000000000E+03 + 73 1.000000000000E-01 1.208000000000E+03 + 73 1.500000000000E-01 4.045000000000E+02 + 73 2.000000000000E-01 1.851000000000E+02 + 73 3.000000000000E-01 6.250000000000E+01 + 73 4.000000000000E-01 2.975000000000E+01 + 73 5.000000000000E-01 1.715000000000E+01 + 73 6.000000000000E-01 1.114000000000E+01 + 73 8.000000000000E-01 5.866000000000E+00 + 73 1.000000000000E+00 3.684000000000E+00 + 73 1.022000000000E+00 3.526000000000E+00 + 73 1.250000000000E+00 2.378000000000E+00 + 73 1.500000000000E+00 1.696000000000E+00 + 73 2.000000000000E+00 1.027000000000E+00 + 73 2.044000000000E+00 9.902000000000E-01 + 73 3.000000000000E+00 5.389000000000E-01 + 73 4.000000000000E+00 3.539000000000E-01 + 73 5.000000000000E+00 2.600000000000E-01 + 73 6.000000000000E+00 2.041000000000E-01 + 73 7.000000000000E+00 1.674000000000E-01 + 73 8.000000000000E+00 1.416000000000E-01 + 73 9.000000000000E+00 1.225000000000E-01 + 73 1.000000000000E+01 1.079000000000E-01 + 74 1.000000000000E-03 1.121000000000E+06 + 74 1.500000000000E-03 4.984000000000E+05 + 74 1.809000000000E-03 3.350000000000E+05 + 74 1.809000100000E-03 3.983000000000E+05 + 74 1.840000000000E-03 5.896000000000E+05 + 74 1.872000000000E-03 8.714000000000E+05 + 74 1.872000100000E-03 9.502000000000E+05 + 74 2.000000000000E-03 1.194000000000E+06 + 74 2.281000000000E-03 8.602000000000E+05 + 74 2.281000100000E-03 9.981000000000E+05 + 74 2.423000000000E-03 8.614000000000E+05 + 74 2.575000000000E-03 7.436000000000E+05 + 74 2.575000100000E-03 7.904000000000E+05 + 74 2.694000000000E-03 7.109000000000E+05 + 74 2.820000000000E-03 6.393000000000E+05 + 74 2.820000100000E-03 6.669000000000E+05 + 74 3.000000000000E-03 5.778000000000E+05 + 74 4.000000000000E-03 2.894000000000E+05 + 74 5.000000000000E-03 1.666000000000E+05 + 74 6.000000000000E-03 1.052000000000E+05 + 74 8.000000000000E-03 5.039000000000E+04 + 74 1.000000000000E-02 2.821000000000E+04 + 74 1.021000000000E-02 2.674000000000E+04 + 74 1.021000010000E-02 6.990000000000E+04 + 74 1.085000000000E-02 5.933000000000E+04 + 74 1.154000000000E-02 5.036000000000E+04 + 74 1.154000010000E-02 6.939000000000E+04 + 74 1.182000000000E-02 6.555000000000E+04 + 74 1.210000000000E-02 6.191000000000E+04 + 74 1.210000010000E-02 7.157000000000E+04 + 74 1.500000000000E-02 4.150000000000E+04 + 74 2.000000000000E-02 1.942000000000E+04 + 74 3.000000000000E-02 6.545000000000E+03 + 74 4.000000000000E-02 2.986000000000E+03 + 74 5.000000000000E-02 1.615000000000E+03 + 74 6.000000000000E-02 9.742000000000E+02 + 74 6.953000000000E-02 6.465000000000E+02 + 74 6.953000010000E-02 3.297000000000E+03 + 74 8.000000000000E-02 2.272000000000E+03 + 74 1.000000000000E-01 1.268000000000E+03 + 74 1.500000000000E-01 4.259000000000E+02 + 74 2.000000000000E-01 1.953000000000E+02 + 74 3.000000000000E-01 6.612000000000E+01 + 74 4.000000000000E-01 3.153000000000E+01 + 74 5.000000000000E-01 1.820000000000E+01 + 74 6.000000000000E-01 1.183000000000E+01 + 74 8.000000000000E-01 6.235000000000E+00 + 74 1.000000000000E+00 3.917000000000E+00 + 74 1.022000000000E+00 3.749000000000E+00 + 74 1.250000000000E+00 2.528000000000E+00 + 74 1.500000000000E+00 1.803000000000E+00 + 74 2.000000000000E+00 1.091000000000E+00 + 74 2.044000000000E+00 1.053000000000E+00 + 74 3.000000000000E+00 5.725000000000E-01 + 74 4.000000000000E+00 3.759000000000E-01 + 74 5.000000000000E+00 2.761000000000E-01 + 74 6.000000000000E+00 2.167000000000E-01 + 74 7.000000000000E+00 1.777000000000E-01 + 74 8.000000000000E+00 1.503000000000E-01 + 74 9.000000000000E+00 1.300000000000E-01 + 74 1.000000000000E+01 1.144000000000E-01 + 75 1.000000000000E-03 1.194000000000E+06 + 75 1.500000000000E-03 5.311000000000E+05 + 75 1.823000000000E-03 3.514000000000E+05 + 75 1.823000100000E-03 3.516000000000E+05 + 75 1.885000000000E-03 5.374000000000E+05 + 75 1.949000000000E-03 8.179000000000E+05 + 75 1.949000100000E-03 8.442000000000E+05 + 75 2.000000000000E-03 1.163000000000E+06 + 75 2.367000000000E-03 8.302000000000E+05 + 75 2.367000100000E-03 9.625000000000E+05 + 75 2.520000000000E-03 8.273000000000E+05 + 75 2.682000000000E-03 7.110000000000E+05 + 75 2.682000100000E-03 7.556000000000E+05 + 75 2.804000000000E-03 6.807000000000E+05 + 75 2.932000000000E-03 6.131000000000E+05 + 75 2.932000100000E-03 6.395000000000E+05 + 75 3.000000000000E-03 6.068000000000E+05 + 75 4.000000000000E-03 3.048000000000E+05 + 75 5.000000000000E-03 1.757000000000E+05 + 75 6.000000000000E-03 1.110000000000E+05 + 75 8.000000000000E-03 5.325000000000E+04 + 75 1.000000000000E-02 2.985000000000E+04 + 75 1.054000000000E-02 2.604000000000E+04 + 75 1.054000010000E-02 6.773000000000E+04 + 75 1.122000000000E-02 5.718000000000E+04 + 75 1.196000000000E-02 4.828000000000E+04 + 75 1.196000010000E-02 6.659000000000E+04 + 75 1.224000000000E-02 6.294000000000E+04 + 75 1.253000000000E-02 5.951000000000E+04 + 75 1.253000010000E-02 6.880000000000E+04 + 75 1.500000000000E-02 4.360000000000E+04 + 75 2.000000000000E-02 2.047000000000E+04 + 75 3.000000000000E-02 6.914000000000E+03 + 75 4.000000000000E-02 3.160000000000E+03 + 75 5.000000000000E-02 1.711000000000E+03 + 75 6.000000000000E-02 1.033000000000E+03 + 75 7.168000000000E-02 6.303000000000E+02 + 75 7.168000010000E-02 3.187000000000E+03 + 75 8.000000000000E-02 2.380000000000E+03 + 75 1.000000000000E-01 1.329000000000E+03 + 75 1.500000000000E-01 4.480000000000E+02 + 75 2.000000000000E-01 2.059000000000E+02 + 75 3.000000000000E-01 6.990000000000E+01 + 75 4.000000000000E-01 3.339000000000E+01 + 75 5.000000000000E-01 1.929000000000E+01 + 75 6.000000000000E-01 1.256000000000E+01 + 75 8.000000000000E-01 6.622000000000E+00 + 75 1.000000000000E+00 4.162000000000E+00 + 75 1.022000000000E+00 3.983000000000E+00 + 75 1.250000000000E+00 2.686000000000E+00 + 75 1.500000000000E+00 1.915000000000E+00 + 75 2.000000000000E+00 1.159000000000E+00 + 75 2.044000000000E+00 1.118000000000E+00 + 75 3.000000000000E+00 6.078000000000E-01 + 75 4.000000000000E+00 3.989000000000E-01 + 75 5.000000000000E+00 2.929000000000E-01 + 75 6.000000000000E+00 2.299000000000E-01 + 75 7.000000000000E+00 1.885000000000E-01 + 75 8.000000000000E+00 1.593000000000E-01 + 75 9.000000000000E+00 1.378000000000E-01 + 75 1.000000000000E+01 1.213000000000E-01 + 76 1.000000000000E-03 1.270000000000E+06 + 76 1.500000000000E-03 5.654000000000E+05 + 76 1.960000000000E-03 3.197000000000E+05 + 76 1.960000100000E-03 3.297000000000E+05 + 76 2.000000000000E-03 6.972000000000E+05 + 76 2.031000000000E-03 8.154000000000E+05 + 76 2.031000100000E-03 8.925000000000E+05 + 76 2.234000000000E-03 8.451000000000E+05 + 76 2.457000000000E-03 8.002000000000E+05 + 76 2.457000100000E-03 9.271000000000E+05 + 76 2.619000000000E-03 7.937000000000E+05 + 76 2.792000000000E-03 6.796000000000E+05 + 76 2.792000100000E-03 7.221000000000E+05 + 76 3.000000000000E-03 6.092000000000E+05 + 76 3.048000000000E-03 5.872000000000E+05 + 76 3.048000100000E-03 6.124000000000E+05 + 76 4.000000000000E-03 3.203000000000E+05 + 76 5.000000000000E-03 1.850000000000E+05 + 76 6.000000000000E-03 1.171000000000E+05 + 76 8.000000000000E-03 5.622000000000E+04 + 76 1.000000000000E-02 3.154000000000E+04 + 76 1.087000000000E-02 2.536000000000E+04 + 76 1.087000010000E-02 6.563000000000E+04 + 76 1.160000000000E-02 5.511000000000E+04 + 76 1.239000000000E-02 4.629000000000E+04 + 76 1.239000010000E-02 6.390000000000E+04 + 76 1.267000000000E-02 6.044000000000E+04 + 76 1.297000000000E-02 5.718000000000E+04 + 76 1.297000010000E-02 6.611000000000E+04 + 76 1.500000000000E-02 4.572000000000E+04 + 76 2.000000000000E-02 2.154000000000E+04 + 76 3.000000000000E-02 7.297000000000E+03 + 76 4.000000000000E-02 3.341000000000E+03 + 76 5.000000000000E-02 1.811000000000E+03 + 76 6.000000000000E-02 1.095000000000E+03 + 76 7.387000000000E-02 6.147000000000E+02 + 76 7.387000010000E-02 3.078000000000E+03 + 76 8.000000000000E-02 2.499000000000E+03 + 76 1.000000000000E-01 1.392000000000E+03 + 76 1.500000000000E-01 4.708000000000E+02 + 76 2.000000000000E-01 2.169000000000E+02 + 76 3.000000000000E-01 7.382000000000E+01 + 76 4.000000000000E-01 3.533000000000E+01 + 76 5.000000000000E-01 2.043000000000E+01 + 76 6.000000000000E-01 1.331000000000E+01 + 76 8.000000000000E-01 7.026000000000E+00 + 76 1.000000000000E+00 4.418000000000E+00 + 76 1.022000000000E+00 4.229000000000E+00 + 76 1.250000000000E+00 2.852000000000E+00 + 76 1.500000000000E+00 2.033000000000E+00 + 76 2.000000000000E+00 1.230000000000E+00 + 76 2.044000000000E+00 1.186000000000E+00 + 76 3.000000000000E+00 6.448000000000E-01 + 76 4.000000000000E+00 4.230000000000E-01 + 76 5.000000000000E+00 3.105000000000E-01 + 76 6.000000000000E+00 2.436000000000E-01 + 76 7.000000000000E+00 1.997000000000E-01 + 76 8.000000000000E+00 1.688000000000E-01 + 76 9.000000000000E+00 1.460000000000E-01 + 76 1.000000000000E+01 1.285000000000E-01 + 77 1.000000000000E-03 1.350000000000E+06 + 77 1.500000000000E-03 6.022000000000E+05 + 77 2.000000000000E-03 3.259000000000E+05 + 77 2.040000000000E-03 3.120000000000E+05 + 77 2.040000100000E-03 3.358000000000E+05 + 77 2.078000000000E-03 5.110000000000E+05 + 77 2.116000000000E-03 7.770000000000E+05 + 77 2.116000100000E-03 8.191000000000E+05 + 77 2.323000000000E-03 7.943000000000E+05 + 77 2.551000000000E-03 7.703000000000E+05 + 77 2.551000100000E-03 8.901000000000E+05 + 77 2.724000000000E-03 7.595000000000E+05 + 77 2.909000000000E-03 6.482000000000E+05 + 77 2.909000100000E-03 6.882000000000E+05 + 77 3.000000000000E-03 6.389000000000E+05 + 77 3.174000000000E-03 5.610000000000E+05 + 77 3.174000100000E-03 5.852000000000E+05 + 77 4.000000000000E-03 3.365000000000E+05 + 77 5.000000000000E-03 1.947000000000E+05 + 77 6.000000000000E-03 1.234000000000E+05 + 77 8.000000000000E-03 5.928000000000E+04 + 77 1.000000000000E-02 3.329000000000E+04 + 77 1.122000000000E-02 2.468000000000E+04 + 77 1.122000010000E-02 6.370000000000E+04 + 77 1.199000000000E-02 5.315000000000E+04 + 77 1.282000000000E-02 4.434000000000E+04 + 77 1.282000010000E-02 6.137000000000E+04 + 77 1.312000000000E-02 5.805000000000E+04 + 77 1.342000000000E-02 5.493000000000E+04 + 77 1.342000010000E-02 6.350000000000E+04 + 77 1.500000000000E-02 4.785000000000E+04 + 77 2.000000000000E-02 2.265000000000E+04 + 77 3.000000000000E-02 7.693000000000E+03 + 77 4.000000000000E-02 3.528000000000E+03 + 77 5.000000000000E-02 1.915000000000E+03 + 77 6.000000000000E-02 1.158000000000E+03 + 77 7.611000000000E-02 5.994000000000E+02 + 77 7.611000010000E-02 2.975000000000E+03 + 77 8.000000000000E-02 2.618000000000E+03 + 77 1.000000000000E-01 1.456000000000E+03 + 77 1.500000000000E-01 4.944000000000E+02 + 77 2.000000000000E-01 2.282000000000E+02 + 77 3.000000000000E-01 7.790000000000E+01 + 77 4.000000000000E-01 3.734000000000E+01 + 77 5.000000000000E-01 2.163000000000E+01 + 77 6.000000000000E-01 1.410000000000E+01 + 77 8.000000000000E-01 7.449000000000E+00 + 77 1.000000000000E+00 4.686000000000E+00 + 77 1.022000000000E+00 4.485000000000E+00 + 77 1.250000000000E+00 3.025000000000E+00 + 77 1.500000000000E+00 2.156000000000E+00 + 77 2.000000000000E+00 1.305000000000E+00 + 77 2.044000000000E+00 1.258000000000E+00 + 77 3.000000000000E+00 6.834000000000E-01 + 77 4.000000000000E+00 4.482000000000E-01 + 77 5.000000000000E+00 3.289000000000E-01 + 77 6.000000000000E+00 2.580000000000E-01 + 77 7.000000000000E+00 2.115000000000E-01 + 77 8.000000000000E+00 1.788000000000E-01 + 77 9.000000000000E+00 1.546000000000E-01 + 77 1.000000000000E+01 1.360000000000E-01 + 78 1.000000000000E-03 1.432000000000E+06 + 78 1.500000000000E-03 6.397000000000E+05 + 78 2.000000000000E-03 3.464000000000E+05 + 78 2.122000000000E-03 3.044000000000E+05 + 78 2.122000100000E-03 3.300000000000E+05 + 78 2.161000000000E-03 4.952000000000E+05 + 78 2.202000000000E-03 7.429000000000E+05 + 78 2.202000100000E-03 7.907000000000E+05 + 78 2.413000000000E-03 7.666000000000E+05 + 78 2.645000000000E-03 7.433000000000E+05 + 78 2.645000100000E-03 8.586000000000E+05 + 78 3.000000000000E-03 6.334000000000E+05 + 78 3.026000000000E-03 6.198000000000E+05 + 78 3.026000100000E-03 6.580000000000E+05 + 78 3.158000000000E-03 5.950000000000E+05 + 78 3.296000000000E-03 5.380000000000E+05 + 78 3.296000100000E-03 5.611000000000E+05 + 78 4.000000000000E-03 3.535000000000E+05 + 78 5.000000000000E-03 2.048000000000E+05 + 78 6.000000000000E-03 1.299000000000E+05 + 78 8.000000000000E-03 6.250000000000E+04 + 78 1.000000000000E-02 3.513000000000E+04 + 78 1.156000000000E-02 2.406000000000E+04 + 78 1.156000010000E-02 6.170000000000E+04 + 78 1.239000000000E-02 5.123000000000E+04 + 78 1.327000000000E-02 4.254000000000E+04 + 78 1.327000010000E-02 5.888000000000E+04 + 78 1.357000000000E-02 5.579000000000E+04 + 78 1.388000000000E-02 5.285000000000E+04 + 78 1.388000010000E-02 6.110000000000E+04 + 78 1.500000000000E-02 5.008000000000E+04 + 78 2.000000000000E-02 2.380000000000E+04 + 78 3.000000000000E-02 8.110000000000E+03 + 78 4.000000000000E-02 3.724000000000E+03 + 78 5.000000000000E-02 2.024000000000E+03 + 78 6.000000000000E-02 1.226000000000E+03 + 78 7.839000000000E-02 5.851000000000E+02 + 78 7.839000010000E-02 2.910000000000E+03 + 78 8.000000000000E-02 2.703000000000E+03 + 78 1.000000000000E-01 1.521000000000E+03 + 78 1.500000000000E-01 5.188000000000E+02 + 78 2.000000000000E-01 2.400000000000E+02 + 78 3.000000000000E-01 8.214000000000E+01 + 78 4.000000000000E-01 3.945000000000E+01 + 78 5.000000000000E-01 2.287000000000E+01 + 78 6.000000000000E-01 1.493000000000E+01 + 78 8.000000000000E-01 7.893000000000E+00 + 78 1.000000000000E+00 4.967000000000E+00 + 78 1.022000000000E+00 4.754000000000E+00 + 78 1.250000000000E+00 3.207000000000E+00 + 78 1.500000000000E+00 2.285000000000E+00 + 78 2.000000000000E+00 1.383000000000E+00 + 78 2.044000000000E+00 1.333000000000E+00 + 78 3.000000000000E+00 7.240000000000E-01 + 78 4.000000000000E+00 4.746000000000E-01 + 78 5.000000000000E+00 3.482000000000E-01 + 78 6.000000000000E+00 2.731000000000E-01 + 78 7.000000000000E+00 2.238000000000E-01 + 78 8.000000000000E+00 1.891000000000E-01 + 78 9.000000000000E+00 1.635000000000E-01 + 78 1.000000000000E+01 1.439000000000E-01 + 79 1.000000000000E-03 1.518000000000E+06 + 79 1.500000000000E-03 6.793000000000E+05 + 79 2.000000000000E-03 3.681000000000E+05 + 79 2.206000000000E-03 2.969000000000E+05 + 79 2.206000100000E-03 3.214000000000E+05 + 79 2.248000000000E-03 4.825000000000E+05 + 79 2.291000000000E-03 7.240000000000E+05 + 79 2.291000100000E-03 7.675000000000E+05 + 79 2.507000000000E-03 7.416000000000E+05 + 79 2.743000000000E-03 7.167000000000E+05 + 79 2.743000100000E-03 8.271000000000E+05 + 79 3.000000000000E-03 6.670000000000E+05 + 79 3.148000000000E-03 5.927000000000E+05 + 79 3.148000100000E-03 6.289000000000E+05 + 79 3.283000000000E-03 5.691000000000E+05 + 79 3.425000000000E-03 5.152000000000E+05 + 79 3.425000100000E-03 5.372000000000E+05 + 79 4.000000000000E-03 3.711000000000E+05 + 79 5.000000000000E-03 2.152000000000E+05 + 79 6.000000000000E-03 1.367000000000E+05 + 79 8.000000000000E-03 6.584000000000E+04 + 79 1.000000000000E-02 3.704000000000E+04 + 79 1.192000000000E-02 2.345000000000E+04 + 79 1.192000010000E-02 5.982000000000E+04 + 79 1.279000000000E-02 4.940000000000E+04 + 79 1.373000000000E-02 4.080000000000E+04 + 79 1.373000010000E-02 5.654000000000E+04 + 79 1.404000000000E-02 5.361000000000E+04 + 79 1.435000000000E-02 5.084000000000E+04 + 79 1.435000010000E-02 5.874000000000E+04 + 79 1.500000000000E-02 5.249000000000E+04 + 79 2.000000000000E-02 2.502000000000E+04 + 79 3.000000000000E-02 8.541000000000E+03 + 79 4.000000000000E-02 3.928000000000E+03 + 79 5.000000000000E-02 2.138000000000E+03 + 79 6.000000000000E-02 1.296000000000E+03 + 79 8.000000000000E-02 5.856000000000E+02 + 79 8.072000000000E-02 5.711000000000E+02 + 79 8.072000010000E-02 2.784000000000E+03 + 79 1.000000000000E-01 1.588000000000E+03 + 79 1.500000000000E-01 5.441000000000E+02 + 79 2.000000000000E-01 2.522000000000E+02 + 79 3.000000000000E-01 8.655000000000E+01 + 79 4.000000000000E-01 4.164000000000E+01 + 79 5.000000000000E-01 2.417000000000E+01 + 79 6.000000000000E-01 1.579000000000E+01 + 79 8.000000000000E-01 8.357000000000E+00 + 79 1.000000000000E+00 5.261000000000E+00 + 79 1.022000000000E+00 5.035000000000E+00 + 79 1.250000000000E+00 3.396000000000E+00 + 79 1.500000000000E+00 2.420000000000E+00 + 79 2.000000000000E+00 1.464000000000E+00 + 79 2.044000000000E+00 1.412000000000E+00 + 79 3.000000000000E+00 7.663000000000E-01 + 79 4.000000000000E+00 5.022000000000E-01 + 79 5.000000000000E+00 3.683000000000E-01 + 79 6.000000000000E+00 2.888000000000E-01 + 79 7.000000000000E+00 2.366000000000E-01 + 79 8.000000000000E+00 2.000000000000E-01 + 79 9.000000000000E+00 1.729000000000E-01 + 79 1.000000000000E+01 1.521000000000E-01 + 80 1.000000000000E-03 1.605000000000E+06 + 80 1.500000000000E-03 7.201000000000E+05 + 80 2.000000000000E-03 3.905000000000E+05 + 80 2.295000000000E-03 2.886000000000E+05 + 80 2.295000100000E-03 3.258000000000E+05 + 80 2.339000000000E-03 4.793000000000E+05 + 80 2.385000000000E-03 7.051000000000E+05 + 80 2.385000100000E-03 7.606000000000E+05 + 80 2.606000000000E-03 7.238000000000E+05 + 80 2.847000000000E-03 6.888000000000E+05 + 80 2.847000100000E-03 7.955000000000E+05 + 80 3.000000000000E-03 7.019000000000E+05 + 80 3.278000000000E-03 5.643000000000E+05 + 80 3.278000100000E-03 5.989000000000E+05 + 80 3.417000000000E-03 5.428000000000E+05 + 80 3.562000000000E-03 4.920000000000E+05 + 80 3.562000100000E-03 5.129000000000E+05 + 80 4.000000000000E-03 3.896000000000E+05 + 80 5.000000000000E-03 2.261000000000E+05 + 80 6.000000000000E-03 1.437000000000E+05 + 80 8.000000000000E-03 6.932000000000E+04 + 80 1.000000000000E-02 3.903000000000E+04 + 80 1.228000000000E-02 2.285000000000E+04 + 80 1.228000010000E-02 5.793000000000E+04 + 80 1.321000000000E-02 4.759000000000E+04 + 80 1.421000000000E-02 3.911000000000E+04 + 80 1.421000010000E-02 5.424000000000E+04 + 80 1.452000000000E-02 5.149000000000E+04 + 80 1.484000000000E-02 4.888000000000E+04 + 80 1.484000010000E-02 5.656000000000E+04 + 80 1.500000000000E-02 5.489000000000E+04 + 80 2.000000000000E-02 2.628000000000E+04 + 80 3.000000000000E-02 8.989000000000E+03 + 80 4.000000000000E-02 4.142000000000E+03 + 80 5.000000000000E-02 2.256000000000E+03 + 80 6.000000000000E-02 1.369000000000E+03 + 80 8.000000000000E-02 6.197000000000E+02 + 80 8.310000000000E-02 5.578000000000E+02 + 80 8.310000010000E-02 2.693000000000E+03 + 80 1.000000000000E-01 1.656000000000E+03 + 80 1.500000000000E-01 5.702000000000E+02 + 80 2.000000000000E-01 2.647000000000E+02 + 80 3.000000000000E-01 9.113000000000E+01 + 80 4.000000000000E-01 4.392000000000E+01 + 80 5.000000000000E-01 2.553000000000E+01 + 80 6.000000000000E-01 1.669000000000E+01 + 80 8.000000000000E-01 8.842000000000E+00 + 80 1.000000000000E+00 5.568000000000E+00 + 80 1.022000000000E+00 5.330000000000E+00 + 80 1.250000000000E+00 3.595000000000E+00 + 80 1.500000000000E+00 2.561000000000E+00 + 80 2.000000000000E+00 1.549000000000E+00 + 80 2.044000000000E+00 1.494000000000E+00 + 80 3.000000000000E+00 8.106000000000E-01 + 80 4.000000000000E+00 5.311000000000E-01 + 80 5.000000000000E+00 3.894000000000E-01 + 80 6.000000000000E+00 3.053000000000E-01 + 80 7.000000000000E+00 2.501000000000E-01 + 80 8.000000000000E+00 2.113000000000E-01 + 80 9.000000000000E+00 1.827000000000E-01 + 80 1.000000000000E+01 1.607000000000E-01 + 81 1.000000000000E-03 1.695000000000E+06 + 81 1.500000000000E-03 7.627000000000E+05 + 81 2.000000000000E-03 4.140000000000E+05 + 81 2.389000000000E-03 2.798000000000E+05 + 81 2.389000100000E-03 3.805000000000E+05 + 81 2.437000000000E-03 5.120000000000E+05 + 81 2.485000000000E-03 6.886000000000E+05 + 81 2.485000100000E-03 7.958000000000E+05 + 81 2.711000000000E-03 7.252000000000E+05 + 81 2.957000000000E-03 6.608000000000E+05 + 81 2.957000100000E-03 7.654000000000E+05 + 81 3.000000000000E-03 7.393000000000E+05 + 81 3.416000000000E-03 5.366000000000E+05 + 81 3.416000100000E-03 5.692000000000E+05 + 81 3.557000000000E-03 5.168000000000E+05 + 81 3.704000000000E-03 4.693000000000E+05 + 81 3.704000100000E-03 4.896000000000E+05 + 81 4.000000000000E-03 4.083000000000E+05 + 81 5.000000000000E-03 2.371000000000E+05 + 81 6.000000000000E-03 1.508000000000E+05 + 81 8.000000000000E-03 7.291000000000E+04 + 81 1.000000000000E-02 4.109000000000E+04 + 81 1.266000000000E-02 2.226000000000E+04 + 81 1.266000010000E-02 5.611000000000E+04 + 81 1.364000000000E-02 4.587000000000E+04 + 81 1.470000000000E-02 3.750000000000E+04 + 81 1.470000010000E-02 5.219000000000E+04 + 81 1.500000000000E-02 4.967000000000E+04 + 81 1.535000000000E-02 4.695000000000E+04 + 81 1.535000010000E-02 5.427000000000E+04 + 81 2.000000000000E-02 2.757000000000E+04 + 81 3.000000000000E-02 9.452000000000E+03 + 81 4.000000000000E-02 4.363000000000E+03 + 81 5.000000000000E-02 2.380000000000E+03 + 81 6.000000000000E-02 1.446000000000E+03 + 81 8.000000000000E-02 6.552000000000E+02 + 81 8.553000000000E-02 5.448000000000E+02 + 81 8.553000010000E-02 2.605000000000E+03 + 81 1.000000000000E-01 1.727000000000E+03 + 81 1.500000000000E-01 5.970000000000E+02 + 81 2.000000000000E-01 2.777000000000E+02 + 81 3.000000000000E-01 9.587000000000E+01 + 81 4.000000000000E-01 4.629000000000E+01 + 81 5.000000000000E-01 2.694000000000E+01 + 81 6.000000000000E-01 1.763000000000E+01 + 81 8.000000000000E-01 9.349000000000E+00 + 81 1.000000000000E+00 5.890000000000E+00 + 81 1.022000000000E+00 5.638000000000E+00 + 81 1.250000000000E+00 3.803000000000E+00 + 81 1.500000000000E+00 2.709000000000E+00 + 81 2.000000000000E+00 1.639000000000E+00 + 81 2.044000000000E+00 1.580000000000E+00 + 81 3.000000000000E+00 8.570000000000E-01 + 81 4.000000000000E+00 5.612000000000E-01 + 81 5.000000000000E+00 4.114000000000E-01 + 81 6.000000000000E+00 3.225000000000E-01 + 81 7.000000000000E+00 2.641000000000E-01 + 81 8.000000000000E+00 2.231000000000E-01 + 81 9.000000000000E+00 1.929000000000E-01 + 81 1.000000000000E+01 1.697000000000E-01 + 82 1.000000000000E-03 1.788000000000E+06 + 82 1.500000000000E-03 8.066000000000E+05 + 82 2.000000000000E-03 4.383000000000E+05 + 82 2.484000000000E-03 2.718000000000E+05 + 82 2.484000100000E-03 4.765000000000E+05 + 82 2.534000000000E-03 5.629000000000E+05 + 82 2.586000000000E-03 6.650000000000E+05 + 82 2.586000100000E-03 8.392000000000E+05 + 82 3.000000000000E-03 6.725000000000E+05 + 82 3.066000000000E-03 6.354000000000E+05 + 82 3.066000100000E-03 7.349000000000E+05 + 82 3.301000000000E-03 6.130000000000E+05 + 82 3.554000000000E-03 5.113000000000E+05 + 82 3.554000100000E-03 5.419000000000E+05 + 82 3.699000000000E-03 4.927000000000E+05 + 82 3.851000000000E-03 4.479000000000E+05 + 82 3.851000100000E-03 4.674000000000E+05 + 82 4.000000000000E-03 4.273000000000E+05 + 82 5.000000000000E-03 2.485000000000E+05 + 82 6.000000000000E-03 1.582000000000E+05 + 82 8.000000000000E-03 7.660000000000E+04 + 82 1.000000000000E-02 4.322000000000E+04 + 82 1.304000000000E-02 2.171000000000E+04 + 82 1.304000010000E-02 5.443000000000E+04 + 82 1.500000000000E-02 3.723000000000E+04 + 82 1.520000000000E-02 3.594000000000E+04 + 82 1.520000010000E-02 4.996000000000E+04 + 82 1.553000000000E-02 4.748000000000E+04 + 82 1.586000000000E-02 4.514000000000E+04 + 82 1.586000010000E-02 5.218000000000E+04 + 82 2.000000000000E-02 2.889000000000E+04 + 82 3.000000000000E-02 9.929000000000E+03 + 82 4.000000000000E-02 4.593000000000E+03 + 82 5.000000000000E-02 2.509000000000E+03 + 82 6.000000000000E-02 1.525000000000E+03 + 82 8.000000000000E-02 6.924000000000E+02 + 82 8.800000000000E-02 5.324000000000E+02 + 82 8.800000010000E-02 2.519000000000E+03 + 82 1.000000000000E-01 1.802000000000E+03 + 82 1.500000000000E-01 6.244000000000E+02 + 82 2.000000000000E-01 2.912000000000E+02 + 82 3.000000000000E-01 1.008000000000E+02 + 82 4.000000000000E-01 4.876000000000E+01 + 82 5.000000000000E-01 2.841000000000E+01 + 82 6.000000000000E-01 1.860000000000E+01 + 82 8.000000000000E-01 9.878000000000E+00 + 82 1.000000000000E+00 6.226000000000E+00 + 82 1.022000000000E+00 5.959000000000E+00 + 82 1.250000000000E+00 4.020000000000E+00 + 82 1.500000000000E+00 2.863000000000E+00 + 82 2.000000000000E+00 1.732000000000E+00 + 82 2.044000000000E+00 1.670000000000E+00 + 82 3.000000000000E+00 9.054000000000E-01 + 82 4.000000000000E+00 5.927000000000E-01 + 82 5.000000000000E+00 4.344000000000E-01 + 82 6.000000000000E+00 3.404000000000E-01 + 82 7.000000000000E+00 2.788000000000E-01 + 82 8.000000000000E+00 2.355000000000E-01 + 82 9.000000000000E+00 2.035000000000E-01 + 82 1.000000000000E+01 1.790000000000E-01 + 83 1.000000000000E-03 1.884000000000E+06 + 83 1.500000000000E-03 8.521000000000E+05 + 83 2.000000000000E-03 4.636000000000E+05 + 83 2.580000000000E-03 2.643000000000E+05 + 83 2.580000100000E-03 6.147000000000E+05 + 83 2.633000000000E-03 6.275000000000E+05 + 83 2.688000000000E-03 6.406000000000E+05 + 83 2.688000100000E-03 8.922000000000E+05 + 83 3.000000000000E-03 7.089000000000E+05 + 83 3.177000000000E-03 6.120000000000E+05 + 83 3.177000100000E-03 7.071000000000E+05 + 83 3.427000000000E-03 5.871000000000E+05 + 83 3.696000000000E-03 4.875000000000E+05 + 83 3.696000100000E-03 5.166000000000E+05 + 83 3.845000000000E-03 4.702000000000E+05 + 83 3.999000000000E-03 4.281000000000E+05 + 83 3.999000100000E-03 4.469000000000E+05 + 83 4.000000000000E-03 4.466000000000E+05 + 83 5.000000000000E-03 2.601000000000E+05 + 83 6.000000000000E-03 1.659000000000E+05 + 83 8.000000000000E-03 8.041000000000E+04 + 83 1.000000000000E-02 4.542000000000E+04 + 83 1.342000000000E-02 2.118000000000E+04 + 83 1.342000010000E-02 5.280000000000E+04 + 83 1.500000000000E-02 3.905000000000E+04 + 83 1.571000000000E-02 3.450000000000E+04 + 83 1.571000010000E-02 4.801000000000E+04 + 83 1.605000000000E-02 4.566000000000E+04 + 83 1.639000000000E-02 4.342000000000E+04 + 83 1.639000010000E-02 5.019000000000E+04 + 83 2.000000000000E-02 3.021000000000E+04 + 83 3.000000000000E-02 1.042000000000E+04 + 83 4.000000000000E-02 4.831000000000E+03 + 83 5.000000000000E-02 2.642000000000E+03 + 83 6.000000000000E-02 1.608000000000E+03 + 83 8.000000000000E-02 7.312000000000E+02 + 83 9.053000000000E-02 5.205000000000E+02 + 83 9.053000010000E-02 2.437000000000E+03 + 83 1.000000000000E-01 1.881000000000E+03 + 83 1.500000000000E-01 6.523000000000E+02 + 83 2.000000000000E-01 3.051000000000E+02 + 83 3.000000000000E-01 1.059000000000E+02 + 83 4.000000000000E-01 5.132000000000E+01 + 83 5.000000000000E-01 2.994000000000E+01 + 83 6.000000000000E-01 1.962000000000E+01 + 83 8.000000000000E-01 1.043000000000E+01 + 83 1.000000000000E+00 6.577000000000E+00 + 83 1.022000000000E+00 6.295000000000E+00 + 83 1.250000000000E+00 4.247000000000E+00 + 83 1.500000000000E+00 3.025000000000E+00 + 83 2.000000000000E+00 1.829000000000E+00 + 83 2.044000000000E+00 1.763000000000E+00 + 83 3.000000000000E+00 9.559000000000E-01 + 83 4.000000000000E+00 6.256000000000E-01 + 83 5.000000000000E+00 4.584000000000E-01 + 83 6.000000000000E+00 3.591000000000E-01 + 83 7.000000000000E+00 2.940000000000E-01 + 83 8.000000000000E+00 2.483000000000E-01 + 83 9.000000000000E+00 2.146000000000E-01 + 83 1.000000000000E+01 1.888000000000E-01 + 84 1.000000000000E-03 1.982000000000E+06 + 84 1.500000000000E-03 8.993000000000E+05 + 84 2.000000000000E-03 4.898000000000E+05 + 84 2.683000000000E-03 2.558000000000E+05 + 84 2.683000100000E-03 7.616000000000E+05 + 84 2.740000000000E-03 6.828000000000E+05 + 84 2.798000000000E-03 6.122000000000E+05 + 84 2.798000100000E-03 9.372000000000E+05 + 84 3.000000000000E-03 7.440000000000E+05 + 84 3.302000000000E-03 5.840000000000E+05 + 84 3.302000100000E-03 6.752000000000E+05 + 84 3.567000000000E-03 5.581000000000E+05 + 84 3.854000000000E-03 4.614000000000E+05 + 84 3.854000100000E-03 4.891000000000E+05 + 84 4.000000000000E-03 4.475000000000E+05 + 84 4.149000000000E-03 4.097000000000E+05 + 84 4.149000100000E-03 4.276000000000E+05 + 84 5.000000000000E-03 2.723000000000E+05 + 84 6.000000000000E-03 1.738000000000E+05 + 84 8.000000000000E-03 8.436000000000E+04 + 84 1.000000000000E-02 4.770000000000E+04 + 84 1.381000000000E-02 2.065000000000E+04 + 84 1.381000010000E-02 5.118000000000E+04 + 84 1.500000000000E-02 4.109000000000E+04 + 84 1.624000000000E-02 3.306000000000E+04 + 84 1.624000010000E-02 4.610000000000E+04 + 84 1.659000000000E-02 4.384000000000E+04 + 84 1.694000000000E-02 4.168000000000E+04 + 84 1.694000010000E-02 4.819000000000E+04 + 84 2.000000000000E-02 3.158000000000E+04 + 84 3.000000000000E-02 1.093000000000E+04 + 84 4.000000000000E-02 5.078000000000E+03 + 84 5.000000000000E-02 2.781000000000E+03 + 84 6.000000000000E-02 1.694000000000E+03 + 84 8.000000000000E-02 7.716000000000E+02 + 84 9.310000000000E-02 5.088000000000E+02 + 84 9.310000010000E-02 2.355000000000E+03 + 84 1.000000000000E-01 1.966000000000E+03 + 84 1.500000000000E-01 6.810000000000E+02 + 84 2.000000000000E-01 3.193000000000E+02 + 84 3.000000000000E-01 1.111000000000E+02 + 84 4.000000000000E-01 5.397000000000E+01 + 84 5.000000000000E-01 3.153000000000E+01 + 84 6.000000000000E-01 2.069000000000E+01 + 84 8.000000000000E-01 1.100000000000E+01 + 84 1.000000000000E+00 6.943000000000E+00 + 84 1.022000000000E+00 6.646000000000E+00 + 84 1.250000000000E+00 4.484000000000E+00 + 84 1.500000000000E+00 3.193000000000E+00 + 84 2.000000000000E+00 1.931000000000E+00 + 84 2.044000000000E+00 1.861000000000E+00 + 84 3.000000000000E+00 1.009000000000E+00 + 84 4.000000000000E+00 6.599000000000E-01 + 84 5.000000000000E+00 4.834000000000E-01 + 84 6.000000000000E+00 3.786000000000E-01 + 84 7.000000000000E+00 3.100000000000E-01 + 84 8.000000000000E+00 2.618000000000E-01 + 84 9.000000000000E+00 2.262000000000E-01 + 84 1.000000000000E+01 1.989000000000E-01 + 85 1.000000000000E-03 2.041000000000E+06 + 85 1.021000000000E-03 1.965000000000E+06 + 85 1.042000000000E-03 1.892000000000E+06 + 85 1.042000100000E-03 1.932000000000E+06 + 85 1.500000000000E-03 9.478000000000E+05 + 85 2.000000000000E-03 5.169000000000E+05 + 85 2.787000000000E-03 2.481000000000E+05 + 85 2.787000100000E-03 8.441000000000E+05 + 85 2.847000000000E-03 7.021000000000E+05 + 85 2.909000000000E-03 5.843000000000E+05 + 85 2.909000100000E-03 9.704000000000E+05 + 85 3.000000000000E-03 7.806000000000E+05 + 85 3.426000000000E-03 5.592000000000E+05 + 85 3.426000100000E-03 6.471000000000E+05 + 85 4.000000000000E-03 4.414000000000E+05 + 85 4.008000000000E-03 4.392000000000E+05 + 85 4.008000100000E-03 4.658000000000E+05 + 85 4.160000000000E-03 4.258000000000E+05 + 85 4.317000000000E-03 3.894000000000E+05 + 85 4.317000100000E-03 4.064000000000E+05 + 85 5.000000000000E-03 2.847000000000E+05 + 85 6.000000000000E-03 1.820000000000E+05 + 85 8.000000000000E-03 8.846000000000E+04 + 85 1.000000000000E-02 5.007000000000E+04 + 85 1.421000000000E-02 2.016000000000E+04 + 85 1.421000010000E-02 4.960000000000E+04 + 85 1.500000000000E-02 4.332000000000E+04 + 85 1.678000000000E-02 3.172000000000E+04 + 85 1.678000010000E-02 4.431000000000E+04 + 85 1.714000000000E-02 4.216000000000E+04 + 85 1.749000000000E-02 4.011000000000E+04 + 85 1.749000010000E-02 4.637000000000E+04 + 85 2.000000000000E-02 3.293000000000E+04 + 85 3.000000000000E-02 1.146000000000E+04 + 85 4.000000000000E-02 5.333000000000E+03 + 85 5.000000000000E-02 2.925000000000E+03 + 85 6.000000000000E-02 1.784000000000E+03 + 85 8.000000000000E-02 8.138000000000E+02 + 85 9.573000000000E-02 4.976000000000E+02 + 85 9.573000010000E-02 2.282000000000E+03 + 85 1.000000000000E-01 2.037000000000E+03 + 85 1.500000000000E-01 7.106000000000E+02 + 85 2.000000000000E-01 3.340000000000E+02 + 85 3.000000000000E-01 1.166000000000E+02 + 85 4.000000000000E-01 5.673000000000E+01 + 85 5.000000000000E-01 3.319000000000E+01 + 85 6.000000000000E-01 2.179000000000E+01 + 85 8.000000000000E-01 1.161000000000E+01 + 85 1.000000000000E+00 7.325000000000E+00 + 85 1.022000000000E+00 7.012000000000E+00 + 85 1.250000000000E+00 4.732000000000E+00 + 85 1.500000000000E+00 3.369000000000E+00 + 85 2.000000000000E+00 2.037000000000E+00 + 85 2.044000000000E+00 1.964000000000E+00 + 85 3.000000000000E+00 1.064000000000E+00 + 85 4.000000000000E+00 6.957000000000E-01 + 85 5.000000000000E+00 5.095000000000E-01 + 85 6.000000000000E+00 3.990000000000E-01 + 85 7.000000000000E+00 3.266000000000E-01 + 85 8.000000000000E+00 2.758000000000E-01 + 85 9.000000000000E+00 2.382000000000E-01 + 85 1.000000000000E+01 2.095000000000E-01 + 86 1.000000000000E-03 2.143000000000E+06 + 86 1.047000000000E-03 1.968000000000E+06 + 86 1.097000000000E-03 1.807000000000E+06 + 86 1.097000100000E-03 1.845000000000E+06 + 86 1.500000000000E-03 9.977000000000E+05 + 86 2.000000000000E-03 5.450000000000E+05 + 86 2.892000000000E-03 2.407000000000E+05 + 86 2.892000100000E-03 8.514000000000E+05 + 86 3.000000000000E-03 5.689000000000E+05 + 86 3.021000000000E-03 5.581000000000E+05 + 86 3.021000100000E-03 9.066000000000E+05 + 86 3.270000000000E-03 7.007000000000E+05 + 86 3.538000000000E-03 5.416000000000E+05 + 86 3.538000100000E-03 6.268000000000E+05 + 86 4.000000000000E-03 4.634000000000E+05 + 86 4.159000000000E-03 4.200000000000E+05 + 86 4.159000100000E-03 4.454000000000E+05 + 86 4.317000000000E-03 4.069000000000E+05 + 86 4.482000000000E-03 3.717000000000E+05 + 86 4.482000100000E-03 3.879000000000E+05 + 86 5.000000000000E-03 2.979000000000E+05 + 86 6.000000000000E-03 1.904000000000E+05 + 86 8.000000000000E-03 9.270000000000E+04 + 86 1.000000000000E-02 5.253000000000E+04 + 86 1.462000000000E-02 1.968000000000E+04 + 86 1.462000010000E-02 4.822000000000E+04 + 86 1.500000000000E-02 4.511000000000E+04 + 86 1.734000000000E-02 3.045000000000E+04 + 86 1.734000010000E-02 4.259000000000E+04 + 86 1.769000000000E-02 4.056000000000E+04 + 86 1.805000000000E-02 3.865000000000E+04 + 86 1.805000010000E-02 4.469000000000E+04 + 86 2.000000000000E-02 3.433000000000E+04 + 86 3.000000000000E-02 1.200000000000E+04 + 86 4.000000000000E-02 5.598000000000E+03 + 86 5.000000000000E-02 3.075000000000E+03 + 86 6.000000000000E-02 1.878000000000E+03 + 86 8.000000000000E-02 8.577000000000E+02 + 86 9.840000000000E-02 4.869000000000E+02 + 86 9.840000010000E-02 2.188000000000E+03 + 86 1.000000000000E-01 2.125000000000E+03 + 86 1.500000000000E-01 7.412000000000E+02 + 86 2.000000000000E-01 3.491000000000E+02 + 86 3.000000000000E-01 1.222000000000E+02 + 86 4.000000000000E-01 5.959000000000E+01 + 86 5.000000000000E-01 3.491000000000E+01 + 86 6.000000000000E-01 2.294000000000E+01 + 86 8.000000000000E-01 1.223000000000E+01 + 86 1.000000000000E+00 7.724000000000E+00 + 86 1.022000000000E+00 7.394000000000E+00 + 86 1.250000000000E+00 4.991000000000E+00 + 86 1.500000000000E+00 3.553000000000E+00 + 86 2.000000000000E+00 2.148000000000E+00 + 86 2.044000000000E+00 2.070000000000E+00 + 86 3.000000000000E+00 1.121000000000E+00 + 86 4.000000000000E+00 7.330000000000E-01 + 86 5.000000000000E+00 5.367000000000E-01 + 86 6.000000000000E+00 4.202000000000E-01 + 86 7.000000000000E+00 3.439000000000E-01 + 86 8.000000000000E+00 2.903000000000E-01 + 86 9.000000000000E+00 2.508000000000E-01 + 86 1.000000000000E+01 2.205000000000E-01 + 87 1.000000000000E-03 2.248000000000E+06 + 87 1.074000000000E-03 1.970000000000E+06 + 87 1.153000000000E-03 1.727000000000E+06 + 87 1.153000100000E-03 1.763000000000E+06 + 87 1.500000000000E-03 1.049000000000E+06 + 87 2.000000000000E-03 5.740000000000E+05 + 87 3.000000000000E-03 2.336000000000E+05 + 87 3.000000100000E-03 7.350000000000E+05 + 87 3.000000000000E-03 7.347000000000E+05 + 87 3.136000000000E-03 5.329000000000E+05 + 87 3.136000100000E-03 8.312000000000E+05 + 87 3.389000000000E-03 6.578000000000E+05 + 87 3.663000000000E-03 5.206000000000E+05 + 87 3.663000100000E-03 6.041000000000E+05 + 87 4.000000000000E-03 4.859000000000E+05 + 87 4.327000000000E-03 3.987000000000E+05 + 87 4.327000100000E-03 4.235000000000E+05 + 87 4.487000000000E-03 3.876000000000E+05 + 87 4.652000000000E-03 3.547000000000E+05 + 87 4.652000100000E-03 3.700000000000E+05 + 87 5.000000000000E-03 3.112000000000E+05 + 87 6.000000000000E-03 1.990000000000E+05 + 87 8.000000000000E-03 9.708000000000E+04 + 87 1.000000000000E-02 5.505000000000E+04 + 87 1.500000000000E-02 1.932000000000E+04 + 87 1.503000000000E-02 1.921000000000E+04 + 87 1.503000010000E-02 4.695000000000E+04 + 87 1.641000000000E-02 3.704000000000E+04 + 87 1.791000000000E-02 2.922000000000E+04 + 87 1.791000010000E-02 4.102000000000E+04 + 87 1.827000000000E-02 3.903000000000E+04 + 87 1.864000000000E-02 3.715000000000E+04 + 87 1.864000010000E-02 4.293000000000E+04 + 87 2.000000000000E-02 3.583000000000E+04 + 87 3.000000000000E-02 1.257000000000E+04 + 87 4.000000000000E-02 5.872000000000E+03 + 87 5.000000000000E-02 3.230000000000E+03 + 87 6.000000000000E-02 1.975000000000E+03 + 87 8.000000000000E-02 9.036000000000E+02 + 87 1.000000000000E-01 4.914000000000E+02 + 87 1.011000000000E-01 4.764000000000E+02 + 87 1.011000001000E-01 2.135000000000E+03 + 87 1.500000000000E-01 7.727000000000E+02 + 87 2.000000000000E-01 3.646000000000E+02 + 87 3.000000000000E-01 1.281000000000E+02 + 87 4.000000000000E-01 6.255000000000E+01 + 87 5.000000000000E-01 3.670000000000E+01 + 87 6.000000000000E-01 2.414000000000E+01 + 87 8.000000000000E-01 1.288000000000E+01 + 87 1.000000000000E+00 8.140000000000E+00 + 87 1.022000000000E+00 7.794000000000E+00 + 87 1.250000000000E+00 5.264000000000E+00 + 87 1.500000000000E+00 3.744000000000E+00 + 87 2.000000000000E+00 2.263000000000E+00 + 87 2.044000000000E+00 2.182000000000E+00 + 87 3.000000000000E+00 1.181000000000E+00 + 87 4.000000000000E+00 7.719000000000E-01 + 87 5.000000000000E+00 5.650000000000E-01 + 87 6.000000000000E+00 4.423000000000E-01 + 87 7.000000000000E+00 3.619000000000E-01 + 87 8.000000000000E+00 3.055000000000E-01 + 87 9.000000000000E+00 2.639000000000E-01 + 87 1.000000000000E+01 2.320000000000E-01 + 88 1.000000000000E-03 2.323000000000E+06 + 88 1.028000000000E-03 2.207000000000E+06 + 88 1.058000000000E-03 2.098000000000E+06 + 88 1.058000100000E-03 2.128000000000E+06 + 88 1.130000000000E-03 1.878000000000E+06 + 88 1.208000000000E-03 1.656000000000E+06 + 88 1.208000100000E-03 1.691000000000E+06 + 88 1.500000000000E-03 1.103000000000E+06 + 88 2.000000000000E-03 6.038000000000E+05 + 88 3.000000000000E-03 2.461000000000E+05 + 88 3.105000000000E-03 2.276000000000E+05 + 88 3.105000100000E-03 6.395000000000E+05 + 88 3.176000000000E-03 5.720000000000E+05 + 88 3.248000000000E-03 5.118000000000E+05 + 88 3.248000100000E-03 7.612000000000E+05 + 88 3.510000000000E-03 6.170000000000E+05 + 88 3.792000000000E-03 5.001000000000E+05 + 88 3.792000100000E-03 5.811000000000E+05 + 88 4.000000000000E-03 5.094000000000E+05 + 88 4.489000000000E-03 3.809000000000E+05 + 88 4.489000100000E-03 4.043000000000E+05 + 88 4.653000000000E-03 3.704000000000E+05 + 88 4.822000000000E-03 3.394000000000E+05 + 88 4.822000100000E-03 3.541000000000E+05 + 88 5.000000000000E-03 3.249000000000E+05 + 88 6.000000000000E-03 2.078000000000E+05 + 88 8.000000000000E-03 1.015000000000E+05 + 88 1.000000000000E-02 5.764000000000E+04 + 88 1.500000000000E-02 2.026000000000E+04 + 88 1.544000000000E-02 1.878000000000E+04 + 88 1.544000010000E-02 4.571000000000E+04 + 88 1.690000000000E-02 3.582000000000E+04 + 88 1.848000000000E-02 2.807000000000E+04 + 88 1.848000010000E-02 3.951000000000E+04 + 88 1.886000000000E-02 3.759000000000E+04 + 88 1.924000000000E-02 3.575000000000E+04 + 88 1.924000010000E-02 4.130000000000E+04 + 88 2.000000000000E-02 3.741000000000E+04 + 88 3.000000000000E-02 1.316000000000E+04 + 88 4.000000000000E-02 6.157000000000E+03 + 88 5.000000000000E-02 3.392000000000E+03 + 88 6.000000000000E-02 2.076000000000E+03 + 88 8.000000000000E-02 9.513000000000E+02 + 88 1.000000000000E-01 5.179000000000E+02 + 88 1.039000000000E-01 4.665000000000E+02 + 88 1.039000001000E-01 2.068000000000E+03 + 88 1.500000000000E-01 8.053000000000E+02 + 88 2.000000000000E-01 3.805000000000E+02 + 88 3.000000000000E-01 1.341000000000E+02 + 88 4.000000000000E-01 6.562000000000E+01 + 88 5.000000000000E-01 3.855000000000E+01 + 88 6.000000000000E-01 2.538000000000E+01 + 88 8.000000000000E-01 1.356000000000E+01 + 88 1.000000000000E+00 8.573000000000E+00 + 88 1.022000000000E+00 8.209000000000E+00 + 88 1.250000000000E+00 5.545000000000E+00 + 88 1.500000000000E+00 3.944000000000E+00 + 88 2.000000000000E+00 2.383000000000E+00 + 88 2.044000000000E+00 2.298000000000E+00 + 88 3.000000000000E+00 1.243000000000E+00 + 88 4.000000000000E+00 8.124000000000E-01 + 88 5.000000000000E+00 5.945000000000E-01 + 88 6.000000000000E+00 4.653000000000E-01 + 88 7.000000000000E+00 3.807000000000E-01 + 88 8.000000000000E+00 3.213000000000E-01 + 88 9.000000000000E+00 2.775000000000E-01 + 88 1.000000000000E+01 2.440000000000E-01 + 89 1.000000000000E-03 2.434000000000E+06 + 89 1.039000000000E-03 2.269000000000E+06 + 89 1.080000000000E-03 2.115000000000E+06 + 89 1.080000100000E-03 2.145000000000E+06 + 89 1.171000000000E-03 1.841000000000E+06 + 89 1.269000000000E-03 1.580000000000E+06 + 89 1.269000100000E-03 1.612000000000E+06 + 89 1.500000000000E-03 1.157000000000E+06 + 89 2.000000000000E-03 6.347000000000E+05 + 89 3.000000000000E-03 2.591000000000E+05 + 89 3.219000000000E-03 2.207000000000E+05 + 89 3.219000100000E-03 5.752000000000E+05 + 89 3.294000000000E-03 5.301000000000E+05 + 89 3.370000000000E-03 4.886000000000E+05 + 89 3.370000100000E-03 7.094000000000E+05 + 89 3.630000000000E-03 5.866000000000E+05 + 89 3.909000000000E-03 4.851000000000E+05 + 89 3.909000100000E-03 5.635000000000E+05 + 89 4.000000000000E-03 5.340000000000E+05 + 89 4.656000000000E-03 3.640000000000E+05 + 89 4.656000100000E-03 3.864000000000E+05 + 89 5.000000000000E-03 3.242000000000E+05 + 89 5.002000000000E-03 3.239000000000E+05 + 89 5.002000100000E-03 3.380000000000E+05 + 89 6.000000000000E-03 2.168000000000E+05 + 89 8.000000000000E-03 1.061000000000E+05 + 89 1.000000000000E-02 6.028000000000E+04 + 89 1.500000000000E-02 2.123000000000E+04 + 89 1.587000000000E-02 1.834000000000E+04 + 89 1.587000010000E-02 4.445000000000E+04 + 89 1.740000000000E-02 3.460000000000E+04 + 89 1.908000000000E-02 2.693000000000E+04 + 89 1.908000010000E-02 3.801000000000E+04 + 89 1.946000000000E-02 3.617000000000E+04 + 89 1.984000000000E-02 3.443000000000E+04 + 89 1.984000010000E-02 3.962000000000E+04 + 89 2.000000000000E-02 3.901000000000E+04 + 89 3.000000000000E-02 1.376000000000E+04 + 89 4.000000000000E-02 6.451000000000E+03 + 89 5.000000000000E-02 3.558000000000E+03 + 89 6.000000000000E-02 2.180000000000E+03 + 89 8.000000000000E-02 1.001000000000E+03 + 89 1.000000000000E-01 5.456000000000E+02 + 89 1.068000000000E-01 4.567000000000E+02 + 89 1.068000001000E-01 2.001000000000E+03 + 89 1.500000000000E-01 8.370000000000E+02 + 89 2.000000000000E-01 3.969000000000E+02 + 89 3.000000000000E-01 1.403000000000E+02 + 89 4.000000000000E-01 6.879000000000E+01 + 89 5.000000000000E-01 4.047000000000E+01 + 89 6.000000000000E-01 2.668000000000E+01 + 89 8.000000000000E-01 1.427000000000E+01 + 89 1.000000000000E+00 9.025000000000E+00 + 89 1.022000000000E+00 8.642000000000E+00 + 89 1.250000000000E+00 5.838000000000E+00 + 89 1.500000000000E+00 4.152000000000E+00 + 89 2.000000000000E+00 2.509000000000E+00 + 89 2.044000000000E+00 2.419000000000E+00 + 89 3.000000000000E+00 1.308000000000E+00 + 89 4.000000000000E+00 8.546000000000E-01 + 89 5.000000000000E+00 6.252000000000E-01 + 89 6.000000000000E+00 4.893000000000E-01 + 89 7.000000000000E+00 4.002000000000E-01 + 89 8.000000000000E+00 3.377000000000E-01 + 89 9.000000000000E+00 2.917000000000E-01 + 89 1.000000000000E+01 2.564000000000E-01 + 90 1.000000000000E-03 2.543000000000E+06 + 90 1.081000000000E-03 2.205000000000E+06 + 90 1.168000000000E-03 1.913000000000E+06 + 90 1.168000100000E-03 1.940000000000E+06 + 90 1.246000000000E-03 1.713000000000E+06 + 90 1.329000000000E-03 1.513000000000E+06 + 90 1.329000100000E-03 1.543000000000E+06 + 90 1.500000000000E-03 1.213000000000E+06 + 90 2.000000000000E-03 6.664000000000E+05 + 90 3.000000000000E-03 2.725000000000E+05 + 90 3.332000000000E-03 2.145000000000E+05 + 90 3.332000100000E-03 5.330000000000E+05 + 90 3.410000000000E-03 4.996000000000E+05 + 90 3.491000000000E-03 4.683000000000E+05 + 90 3.491000100000E-03 6.701000000000E+05 + 90 4.000000000000E-03 4.791000000000E+05 + 90 4.046000000000E-03 4.653000000000E+05 + 90 4.046000100000E-03 5.414000000000E+05 + 90 4.421000000000E-03 4.337000000000E+05 + 90 4.830000000000E-03 3.474000000000E+05 + 90 4.830000100000E-03 3.686000000000E+05 + 90 5.000000000000E-03 3.387000000000E+05 + 90 5.182000000000E-03 3.100000000000E+05 + 90 5.182000100000E-03 3.234000000000E+05 + 90 6.000000000000E-03 2.261000000000E+05 + 90 8.000000000000E-03 1.107000000000E+05 + 90 1.000000000000E-02 6.300000000000E+04 + 90 1.500000000000E-02 2.224000000000E+04 + 90 1.630000000000E-02 1.793000000000E+04 + 90 1.630000010000E-02 4.325000000000E+04 + 90 1.792000000000E-02 3.344000000000E+04 + 90 1.969000000000E-02 2.586000000000E+04 + 90 1.969000010000E-02 3.698000000000E+04 + 90 2.000000000000E-02 3.506000000000E+04 + 90 2.047000000000E-02 3.310000000000E+04 + 90 2.047000010000E-02 3.822000000000E+04 + 90 3.000000000000E-02 1.437000000000E+04 + 90 4.000000000000E-02 6.754000000000E+03 + 90 5.000000000000E-02 3.731000000000E+03 + 90 6.000000000000E-02 2.288000000000E+03 + 90 8.000000000000E-02 1.052000000000E+03 + 90 1.000000000000E-01 5.744000000000E+02 + 90 1.097000000000E-01 4.473000000000E+02 + 90 1.097000001000E-01 1.939000000000E+03 + 90 1.500000000000E-01 8.702000000000E+02 + 90 2.000000000000E-01 4.138000000000E+02 + 90 3.000000000000E-01 1.467000000000E+02 + 90 4.000000000000E-01 7.208000000000E+01 + 90 5.000000000000E-01 4.247000000000E+01 + 90 6.000000000000E-01 2.802000000000E+01 + 90 8.000000000000E-01 1.501000000000E+01 + 90 1.000000000000E+00 9.495000000000E+00 + 90 1.022000000000E+00 9.092000000000E+00 + 90 1.250000000000E+00 6.144000000000E+00 + 90 1.500000000000E+00 4.369000000000E+00 + 90 2.000000000000E+00 2.640000000000E+00 + 90 2.044000000000E+00 2.545000000000E+00 + 90 3.000000000000E+00 1.376000000000E+00 + 90 4.000000000000E+00 8.986000000000E-01 + 90 5.000000000000E+00 6.572000000000E-01 + 90 6.000000000000E+00 5.142000000000E-01 + 90 7.000000000000E+00 4.205000000000E-01 + 90 8.000000000000E+00 3.549000000000E-01 + 90 9.000000000000E+00 3.064000000000E-01 + 90 1.000000000000E+01 2.693000000000E-01 + 91 1.000000000000E-03 2.500000000000E+06 + 91 1.003000000000E-03 2.485000000000E+06 + 91 1.007000000000E-03 2.470000000000E+06 + 91 1.007000100000E-03 2.630000000000E+06 + 91 1.110000000000E-03 2.196000000000E+06 + 91 1.224000000000E-03 1.835000000000E+06 + 91 1.224000100000E-03 1.861000000000E+06 + 91 1.303000000000E-03 1.647000000000E+06 + 91 1.387000000000E-03 1.459000000000E+06 + 91 1.387000100000E-03 1.488000000000E+06 + 91 1.500000000000E-03 1.271000000000E+06 + 91 2.000000000000E-03 6.987000000000E+05 + 91 3.000000000000E-03 2.857000000000E+05 + 91 3.442000000000E-03 2.088000000000E+05 + 91 3.442000100000E-03 5.187000000000E+05 + 91 3.525000000000E-03 4.835000000000E+05 + 91 3.611000000000E-03 4.507000000000E+05 + 91 3.611000100000E-03 6.461000000000E+05 + 91 4.000000000000E-03 5.005000000000E+05 + 91 4.174000000000E-03 4.498000000000E+05 + 91 4.174000100000E-03 5.236000000000E+05 + 91 5.000000000000E-03 3.326000000000E+05 + 91 5.001000000000E-03 3.325000000000E+05 + 91 5.001000100000E-03 3.529000000000E+05 + 91 5.181000000000E-03 3.234000000000E+05 + 91 5.367000000000E-03 2.964000000000E+05 + 91 5.367000100000E-03 3.091000000000E+05 + 91 6.000000000000E-03 2.354000000000E+05 + 91 8.000000000000E-03 1.154000000000E+05 + 91 1.000000000000E-02 6.574000000000E+04 + 91 1.500000000000E-02 2.325000000000E+04 + 91 1.673000000000E-02 1.751000000000E+04 + 91 1.673000010000E-02 4.206000000000E+04 + 91 2.000000000000E-02 2.589000000000E+04 + 91 2.031000000000E-02 2.484000000000E+04 + 91 2.031000010000E-02 3.522000000000E+04 + 91 2.071000000000E-02 3.352000000000E+04 + 91 2.110000000000E-02 3.191000000000E+04 + 91 2.110000010000E-02 3.683000000000E+04 + 91 3.000000000000E-02 1.500000000000E+04 + 91 4.000000000000E-02 7.064000000000E+03 + 91 5.000000000000E-02 3.908000000000E+03 + 91 6.000000000000E-02 2.400000000000E+03 + 91 8.000000000000E-02 1.106000000000E+03 + 91 1.000000000000E-01 6.043000000000E+02 + 91 1.126000000000E-01 4.380000000000E+02 + 91 1.126000001000E-01 1.878000000000E+03 + 91 1.500000000000E-01 9.038000000000E+02 + 91 2.000000000000E-01 4.312000000000E+02 + 91 3.000000000000E-01 1.533000000000E+02 + 91 4.000000000000E-01 7.547000000000E+01 + 91 5.000000000000E-01 4.453000000000E+01 + 91 6.000000000000E-01 2.941000000000E+01 + 91 8.000000000000E-01 1.577000000000E+01 + 91 1.000000000000E+00 9.985000000000E+00 + 91 1.022000000000E+00 9.561000000000E+00 + 91 1.250000000000E+00 6.462000000000E+00 + 91 1.500000000000E+00 4.595000000000E+00 + 91 2.000000000000E+00 2.776000000000E+00 + 91 2.044000000000E+00 2.676000000000E+00 + 91 3.000000000000E+00 1.446000000000E+00 + 91 4.000000000000E+00 9.442000000000E-01 + 91 5.000000000000E+00 6.905000000000E-01 + 91 6.000000000000E+00 5.401000000000E-01 + 91 7.000000000000E+00 4.417000000000E-01 + 91 8.000000000000E+00 3.726000000000E-01 + 91 9.000000000000E+00 3.217000000000E-01 + 91 1.000000000000E+01 2.828000000000E-01 + 92 1.000000000000E-03 2.614000000000E+06 + 92 1.022000000000E-03 2.514000000000E+06 + 92 1.045000000000E-03 2.417000000000E+06 + 92 1.045000100000E-03 2.572000000000E+06 + 92 1.153000000000E-03 2.142000000000E+06 + 92 1.273000000000E-03 1.784000000000E+06 + 92 1.273000100000E-03 1.809000000000E+06 + 92 1.354000000000E-03 1.601000000000E+06 + 92 1.441000000000E-03 1.417000000000E+06 + 92 1.441000100000E-03 1.445000000000E+06 + 92 1.500000000000E-03 1.331000000000E+06 + 92 2.000000000000E-03 7.323000000000E+05 + 92 3.000000000000E-03 2.997000000000E+05 + 92 3.552000000000E-03 2.037000000000E+05 + 92 3.552000100000E-03 4.962000000000E+05 + 92 3.639000000000E-03 4.648000000000E+05 + 92 3.728000000000E-03 4.355000000000E+05 + 92 3.728000100000E-03 6.212000000000E+05 + 92 4.000000000000E-03 5.213000000000E+05 + 92 4.303000000000E-03 4.351000000000E+05 + 92 4.303000100000E-03 5.067000000000E+05 + 92 5.000000000000E-03 3.479000000000E+05 + 92 5.182000000000E-03 3.174000000000E+05 + 92 5.182000100000E-03 3.369000000000E+05 + 92 5.362000000000E-03 3.096000000000E+05 + 92 5.548000000000E-03 2.845000000000E+05 + 92 5.548000100000E-03 2.967000000000E+05 + 92 6.000000000000E-03 2.452000000000E+05 + 92 8.000000000000E-03 1.202000000000E+05 + 92 1.000000000000E-02 6.860000000000E+04 + 92 1.500000000000E-02 2.430000000000E+04 + 92 1.717000000000E-02 1.713000000000E+04 + 92 1.717000010000E-02 4.098000000000E+04 + 92 2.000000000000E-02 2.700000000000E+04 + 92 2.095000000000E-02 2.387000000000E+04 + 92 2.095000010000E-02 3.390000000000E+04 + 92 2.135000000000E-02 3.228000000000E+04 + 92 2.176000000000E-02 3.073000000000E+04 + 92 2.176000010000E-02 3.546000000000E+04 + 92 3.000000000000E-02 1.566000000000E+04 + 92 4.000000000000E-02 7.383000000000E+03 + 92 5.000000000000E-02 4.092000000000E+03 + 92 6.000000000000E-02 2.515000000000E+03 + 92 8.000000000000E-02 1.161000000000E+03 + 92 1.000000000000E-01 6.354000000000E+02 + 92 1.156000000000E-01 4.292000000000E+02 + 92 1.156000001000E-01 1.819000000000E+03 + 92 1.500000000000E-01 9.381000000000E+02 + 92 2.000000000000E-01 4.489000000000E+02 + 92 3.000000000000E-01 1.601000000000E+02 + 92 4.000000000000E-01 7.899000000000E+01 + 92 5.000000000000E-01 4.667000000000E+01 + 92 6.000000000000E-01 3.086000000000E+01 + 92 8.000000000000E-01 1.657000000000E+01 + 92 1.000000000000E+00 1.049000000000E+01 + 92 1.022000000000E+00 1.005000000000E+01 + 92 1.250000000000E+00 6.792000000000E+00 + 92 1.500000000000E+00 4.830000000000E+00 + 92 2.000000000000E+00 2.917000000000E+00 + 92 2.044000000000E+00 2.812000000000E+00 + 92 3.000000000000E+00 1.520000000000E+00 + 92 4.000000000000E+00 9.918000000000E-01 + 92 5.000000000000E+00 7.251000000000E-01 + 92 6.000000000000E+00 5.671000000000E-01 + 92 7.000000000000E+00 4.637000000000E-01 + 92 8.000000000000E+00 3.911000000000E-01 + 92 9.000000000000E+00 3.377000000000E-01 + 92 1.000000000000E+01 2.968000000000E-01 + 93 1.000000000000E-03 2.731000000000E+06 + 93 1.042000000000E-03 2.536000000000E+06 + 93 1.087000000000E-03 2.355000000000E+06 + 93 1.087000100000E-03 2.506000000000E+06 + 93 1.201000000000E-03 2.076000000000E+06 + 93 1.328000000000E-03 1.720000000000E+06 + 93 1.328000100000E-03 1.743000000000E+06 + 93 1.500000000000E-03 1.368000000000E+06 + 93 1.501000000000E-03 1.367000000000E+06 + 93 1.501000100000E-03 1.393000000000E+06 + 93 2.000000000000E-03 7.672000000000E+05 + 93 3.000000000000E-03 3.140000000000E+05 + 93 3.666000000000E-03 1.985000000000E+05 + 93 3.666000100000E-03 4.745000000000E+05 + 93 3.757000000000E-03 4.463000000000E+05 + 93 3.850000000000E-03 4.199000000000E+05 + 93 3.850000100000E-03 5.951000000000E+05 + 93 4.000000000000E-03 5.432000000000E+05 + 93 4.435000000000E-03 4.210000000000E+05 + 93 4.435000100000E-03 4.906000000000E+05 + 93 5.000000000000E-03 3.634000000000E+05 + 93 5.366000000000E-03 3.034000000000E+05 + 93 5.366000100000E-03 3.220000000000E+05 + 93 5.542000000000E-03 2.973000000000E+05 + 93 5.723000000000E-03 2.745000000000E+05 + 93 5.723000100000E-03 2.862000000000E+05 + 93 6.000000000000E-03 2.553000000000E+05 + 93 8.000000000000E-03 1.252000000000E+05 + 93 1.000000000000E-02 7.154000000000E+04 + 93 1.500000000000E-02 2.538000000000E+04 + 93 1.761000000000E-02 1.676000000000E+04 + 93 1.761000010000E-02 3.990000000000E+04 + 93 2.000000000000E-02 2.819000000000E+04 + 93 2.160000000000E-02 2.292000000000E+04 + 93 2.160000010000E-02 3.265000000000E+04 + 93 2.201000000000E-02 3.108000000000E+04 + 93 2.243000000000E-02 2.958000000000E+04 + 93 2.243000010000E-02 3.413000000000E+04 + 93 3.000000000000E-02 1.631000000000E+04 + 93 4.000000000000E-02 7.711000000000E+03 + 93 5.000000000000E-02 4.281000000000E+03 + 93 6.000000000000E-02 2.635000000000E+03 + 93 8.000000000000E-02 1.218000000000E+03 + 93 1.000000000000E-01 6.677000000000E+02 + 93 1.187000000000E-01 4.206000000000E+02 + 93 1.187000001000E-01 1.763000000000E+03 + 93 1.500000000000E-01 9.732000000000E+02 + 93 2.000000000000E-01 4.670000000000E+02 + 93 3.000000000000E-01 1.671000000000E+02 + 93 4.000000000000E-01 8.261000000000E+01 + 93 5.000000000000E-01 4.889000000000E+01 + 93 6.000000000000E-01 3.236000000000E+01 + 93 8.000000000000E-01 1.740000000000E+01 + 93 1.000000000000E+00 1.102000000000E+01 + 93 1.022000000000E+00 1.056000000000E+01 + 93 1.250000000000E+00 7.137000000000E+00 + 93 1.500000000000E+00 5.074000000000E+00 + 93 2.000000000000E+00 3.065000000000E+00 + 93 2.044000000000E+00 2.954000000000E+00 + 93 3.000000000000E+00 1.596000000000E+00 + 93 4.000000000000E+00 1.041000000000E+00 + 93 5.000000000000E+00 7.611000000000E-01 + 93 6.000000000000E+00 5.951000000000E-01 + 93 7.000000000000E+00 4.865000000000E-01 + 93 8.000000000000E+00 4.104000000000E-01 + 93 9.000000000000E+00 3.542000000000E-01 + 93 1.000000000000E+01 3.113000000000E-01 + 94 1.000000000000E-03 2.850000000000E+06 + 94 1.056000000000E-03 2.589000000000E+06 + 94 1.115000000000E-03 2.352000000000E+06 + 94 1.115000100000E-03 2.502000000000E+06 + 94 1.237000000000E-03 2.055000000000E+06 + 94 1.372000000000E-03 1.688000000000E+06 + 94 1.372000100000E-03 1.711000000000E+06 + 94 1.500000000000E-03 1.432000000000E+06 + 94 1.559000000000E-03 1.326000000000E+06 + 94 1.559000100000E-03 1.351000000000E+06 + 94 2.000000000000E-03 8.033000000000E+05 + 94 3.000000000000E-03 3.286000000000E+05 + 94 3.778000000000E-03 1.937000000000E+05 + 94 3.778000100000E-03 4.645000000000E+05 + 94 3.874000000000E-03 4.341000000000E+05 + 94 3.973000000000E-03 4.057000000000E+05 + 94 3.973000100000E-03 5.501000000000E+05 + 94 4.000000000000E-03 5.621000000000E+05 + 94 4.557000000000E-03 4.103000000000E+05 + 94 4.557000100000E-03 4.782000000000E+05 + 94 5.000000000000E-03 3.793000000000E+05 + 94 5.541000000000E-03 2.915000000000E+05 + 94 5.541000100000E-03 3.093000000000E+05 + 94 5.734000000000E-03 2.843000000000E+05 + 94 5.933000000000E-03 2.613000000000E+05 + 94 5.933000100000E-03 2.725000000000E+05 + 94 6.000000000000E-03 2.650000000000E+05 + 94 8.000000000000E-03 1.303000000000E+05 + 94 1.000000000000E-02 7.452000000000E+04 + 94 1.500000000000E-02 2.647000000000E+04 + 94 1.806000000000E-02 1.639000000000E+04 + 94 1.806000010000E-02 3.885000000000E+04 + 94 2.000000000000E-02 2.945000000000E+04 + 94 2.227000000000E-02 2.203000000000E+04 + 94 2.227000010000E-02 3.142000000000E+04 + 94 2.268000000000E-02 2.995000000000E+04 + 94 2.310000000000E-02 2.855000000000E+04 + 94 2.310000010000E-02 3.293000000000E+04 + 94 3.000000000000E-02 1.698000000000E+04 + 94 4.000000000000E-02 8.045000000000E+03 + 94 5.000000000000E-02 4.475000000000E+03 + 94 6.000000000000E-02 2.758000000000E+03 + 94 8.000000000000E-02 1.278000000000E+03 + 94 1.000000000000E-01 7.012000000000E+02 + 94 1.218000000000E-01 4.121000000000E+02 + 94 1.218000001000E-01 1.708000000000E+03 + 94 1.500000000000E-01 1.009000000000E+03 + 94 2.000000000000E-01 4.854000000000E+02 + 94 3.000000000000E-01 1.742000000000E+02 + 94 4.000000000000E-01 8.636000000000E+01 + 94 5.000000000000E-01 5.118000000000E+01 + 94 6.000000000000E-01 3.392000000000E+01 + 94 8.000000000000E-01 1.826000000000E+01 + 94 1.000000000000E+00 1.157000000000E+01 + 94 1.022000000000E+00 1.109000000000E+01 + 94 1.250000000000E+00 7.495000000000E+00 + 94 1.500000000000E+00 5.329000000000E+00 + 94 2.000000000000E+00 3.218000000000E+00 + 94 2.044000000000E+00 3.102000000000E+00 + 94 3.000000000000E+00 1.675000000000E+00 + 94 4.000000000000E+00 1.093000000000E+00 + 94 5.000000000000E+00 7.985000000000E-01 + 94 6.000000000000E+00 6.243000000000E-01 + 94 7.000000000000E+00 5.102000000000E-01 + 94 8.000000000000E+00 4.303000000000E-01 + 94 9.000000000000E+00 3.714000000000E-01 + 94 1.000000000000E+01 3.264000000000E-01 + 95 1.000000000000E-03 2.968000000000E+06 + 95 1.066000000000E-03 2.655000000000E+06 + 95 1.136000000000E-03 2.376000000000E+06 + 95 1.136000100000E-03 2.525000000000E+06 + 95 1.266000000000E-03 2.053000000000E+06 + 95 1.412000000000E-03 1.670000000000E+06 + 95 1.412000100000E-03 1.691000000000E+06 + 95 1.500000000000E-03 1.498000000000E+06 + 95 1.617000000000E-03 1.286000000000E+06 + 95 1.617000100000E-03 1.310000000000E+06 + 95 2.000000000000E-03 8.404000000000E+05 + 95 3.000000000000E-03 3.439000000000E+05 + 95 3.887000000000E-03 1.898000000000E+05 + 95 3.887000100000E-03 4.442000000000E+05 + 95 4.000000000000E-03 4.192000000000E+05 + 95 4.092000000000E-03 3.934000000000E+05 + 95 4.092000100000E-03 5.552000000000E+05 + 95 4.370000000000E-03 4.729000000000E+05 + 95 4.667000000000E-03 4.029000000000E+05 + 95 4.667000100000E-03 4.695000000000E+05 + 95 5.000000000000E-03 3.957000000000E+05 + 95 5.710000000000E-03 2.818000000000E+05 + 95 5.710000100000E-03 2.987000000000E+05 + 95 6.000000000000E-03 2.646000000000E+05 + 95 6.121000000000E-03 2.517000000000E+05 + 95 6.121000100000E-03 2.623000000000E+05 + 95 8.000000000000E-03 1.356000000000E+05 + 95 1.000000000000E-02 7.758000000000E+04 + 95 1.500000000000E-02 2.760000000000E+04 + 95 1.850000000000E-02 1.606000000000E+04 + 95 1.850000010000E-02 3.787000000000E+04 + 95 2.000000000000E-02 3.085000000000E+04 + 95 2.294000000000E-02 2.119000000000E+04 + 95 2.294000010000E-02 3.027000000000E+04 + 95 2.335000000000E-02 2.889000000000E+04 + 95 2.377000000000E-02 2.758000000000E+04 + 95 2.377000010000E-02 3.180000000000E+04 + 95 3.000000000000E-02 1.766000000000E+04 + 95 4.000000000000E-02 8.391000000000E+03 + 95 5.000000000000E-02 4.676000000000E+03 + 95 6.000000000000E-02 2.886000000000E+03 + 95 8.000000000000E-02 1.339000000000E+03 + 95 1.000000000000E-01 7.361000000000E+02 + 95 1.250000000000E-01 4.039000000000E+02 + 95 1.250000001000E-01 1.654000000000E+03 + 95 1.500000000000E-01 1.046000000000E+03 + 95 2.000000000000E-01 5.041000000000E+02 + 95 3.000000000000E-01 1.816000000000E+02 + 95 4.000000000000E-01 9.022000000000E+01 + 95 5.000000000000E-01 5.356000000000E+01 + 95 6.000000000000E-01 3.553000000000E+01 + 95 8.000000000000E-01 1.915000000000E+01 + 95 1.000000000000E+00 1.215000000000E+01 + 95 1.022000000000E+00 1.163000000000E+01 + 95 1.250000000000E+00 7.868000000000E+00 + 95 1.500000000000E+00 5.594000000000E+00 + 95 2.000000000000E+00 3.378000000000E+00 + 95 2.044000000000E+00 3.256000000000E+00 + 95 3.000000000000E+00 1.758000000000E+00 + 95 4.000000000000E+00 1.146000000000E+00 + 95 5.000000000000E+00 8.374000000000E-01 + 95 6.000000000000E+00 6.545000000000E-01 + 95 7.000000000000E+00 5.349000000000E-01 + 95 8.000000000000E+00 4.511000000000E-01 + 95 9.000000000000E+00 3.893000000000E-01 + 95 1.000000000000E+01 3.420000000000E-01 + 96 1.000000000000E-03 3.087000000000E+06 + 96 1.074000000000E-03 2.725000000000E+06 + 96 1.154000000000E-03 2.407000000000E+06 + 96 1.154000100000E-03 2.555000000000E+06 + 96 1.289000000000E-03 2.070000000000E+06 + 96 1.440000000000E-03 1.678000000000E+06 + 96 1.440000100000E-03 1.712000000000E+06 + 96 1.500000000000E-03 1.566000000000E+06 + 96 1.643000000000E-03 1.301000000000E+06 + 96 1.643000100000E-03 1.325000000000E+06 + 96 2.000000000000E-03 8.782000000000E+05 + 96 3.000000000000E-03 3.599000000000E+05 + 96 3.971000000000E-03 1.890000000000E+05 + 96 3.971000100000E-03 4.258000000000E+05 + 96 4.000000000000E-03 4.213000000000E+05 + 96 4.227000000000E-03 3.782000000000E+05 + 96 4.227000100000E-03 5.282000000000E+05 + 96 4.503000000000E-03 4.550000000000E+05 + 96 4.797000000000E-03 3.919000000000E+05 + 96 4.797000100000E-03 4.562000000000E+05 + 96 5.000000000000E-03 4.131000000000E+05 + 96 5.895000000000E-03 2.710000000000E+05 + 96 5.895000100000E-03 2.874000000000E+05 + 96 6.000000000000E-03 2.751000000000E+05 + 96 6.288000000000E-03 2.449000000000E+05 + 96 6.288000100000E-03 2.551000000000E+05 + 96 8.000000000000E-03 1.410000000000E+05 + 96 1.000000000000E-02 8.070000000000E+04 + 96 1.500000000000E-02 2.876000000000E+04 + 96 1.893000000000E-02 1.579000000000E+04 + 96 1.893000010000E-02 3.708000000000E+04 + 96 2.000000000000E-02 3.221000000000E+04 + 96 2.380000000000E-02 2.002000000000E+04 + 96 2.380000010000E-02 2.866000000000E+04 + 96 2.413000000000E-02 2.763000000000E+04 + 96 2.446000000000E-02 2.665000000000E+04 + 96 2.446000010000E-02 3.071000000000E+04 + 96 3.000000000000E-02 1.835000000000E+04 + 96 4.000000000000E-02 8.749000000000E+03 + 96 5.000000000000E-02 4.884000000000E+03 + 96 6.000000000000E-02 3.019000000000E+03 + 96 8.000000000000E-02 1.404000000000E+03 + 96 1.000000000000E-01 7.724000000000E+02 + 96 1.282000000000E-01 3.967000000000E+02 + 96 1.282000001000E-01 1.604000000000E+03 + 96 1.500000000000E-01 1.084000000000E+03 + 96 2.000000000000E-01 5.233000000000E+02 + 96 3.000000000000E-01 1.892000000000E+02 + 96 4.000000000000E-01 9.420000000000E+01 + 96 5.000000000000E-01 5.601000000000E+01 + 96 6.000000000000E-01 3.720000000000E+01 + 96 8.000000000000E-01 2.008000000000E+01 + 96 1.000000000000E+00 1.274000000000E+01 + 96 1.022000000000E+00 1.221000000000E+01 + 96 1.250000000000E+00 8.256000000000E+00 + 96 1.500000000000E+00 5.869000000000E+00 + 96 2.000000000000E+00 3.543000000000E+00 + 96 2.044000000000E+00 3.416000000000E+00 + 96 3.000000000000E+00 1.844000000000E+00 + 96 4.000000000000E+00 1.202000000000E+00 + 96 5.000000000000E+00 8.777000000000E-01 + 96 6.000000000000E+00 6.860000000000E-01 + 96 7.000000000000E+00 5.605000000000E-01 + 96 8.000000000000E+00 4.726000000000E-01 + 96 9.000000000000E+00 4.079000000000E-01 + 96 1.000000000000E+01 3.583000000000E-01 + 97 1.000000000000E-03 3.211000000000E+06 + 97 1.111000000000E-03 2.666000000000E+06 + 97 1.235000000000E-03 2.213000000000E+06 + 97 1.235000100000E-03 2.354000000000E+06 + 97 1.500000000000E-03 1.616000000000E+06 + 97 1.554000000000E-03 1.504000000000E+06 + 97 1.554000100000E-03 1.523000000000E+06 + 97 1.651000000000E-03 1.344000000000E+06 + 97 1.755000000000E-03 1.186000000000E+06 + 97 1.755000100000E-03 1.208000000000E+06 + 97 2.000000000000E-03 9.174000000000E+05 + 97 3.000000000000E-03 3.762000000000E+05 + 97 4.000000000000E-03 1.943000000000E+05 + 97 4.132000000000E-03 1.801000000000E+05 + 97 4.132000100000E-03 4.030000000000E+05 + 97 4.247000000000E-03 3.828000000000E+05 + 97 4.366000000000E-03 3.637000000000E+05 + 97 4.366000100000E-03 5.059000000000E+05 + 97 4.661000000000E-03 4.336000000000E+05 + 97 4.977000000000E-03 3.717000000000E+05 + 97 4.977000100000E-03 4.359000000000E+05 + 97 5.000000000000E-03 4.291000000000E+05 + 97 6.000000000000E-03 2.707000000000E+05 + 97 6.147000000000E-03 2.540000000000E+05 + 97 6.147000100000E-03 2.694000000000E+05 + 97 6.348000000000E-03 2.486000000000E+05 + 97 6.556000000000E-03 2.295000000000E+05 + 97 6.556000100000E-03 2.392000000000E+05 + 97 8.000000000000E-03 1.465000000000E+05 + 97 1.000000000000E-02 8.384000000000E+04 + 97 1.500000000000E-02 2.995000000000E+04 + 97 1.945000000000E-02 1.534000000000E+04 + 97 1.945000010000E-02 3.600000000000E+04 + 97 2.000000000000E-02 3.319000000000E+04 + 97 2.439000000000E-02 1.952000000000E+04 + 97 2.439000010000E-02 2.803000000000E+04 + 97 2.483000000000E-02 2.672000000000E+04 + 97 2.527000000000E-02 2.547000000000E+04 + 97 2.527000010000E-02 2.936000000000E+04 + 97 3.000000000000E-02 1.905000000000E+04 + 97 4.000000000000E-02 9.118000000000E+03 + 97 5.000000000000E-02 5.098000000000E+03 + 97 6.000000000000E-02 3.155000000000E+03 + 97 8.000000000000E-02 1.470000000000E+03 + 97 1.000000000000E-01 8.100000000000E+02 + 97 1.316000000000E-01 3.887000000000E+02 + 97 1.316000001000E-01 1.554000000000E+03 + 97 1.500000000000E-01 1.123000000000E+03 + 97 2.000000000000E-01 5.429000000000E+02 + 97 3.000000000000E-01 1.970000000000E+02 + 97 4.000000000000E-01 9.831000000000E+01 + 97 5.000000000000E-01 5.855000000000E+01 + 97 6.000000000000E-01 3.893000000000E+01 + 97 8.000000000000E-01 2.104000000000E+01 + 97 1.000000000000E+00 1.336000000000E+01 + 97 1.022000000000E+00 1.280000000000E+01 + 97 1.250000000000E+00 8.661000000000E+00 + 97 1.500000000000E+00 6.155000000000E+00 + 97 2.000000000000E+00 3.716000000000E+00 + 97 2.044000000000E+00 3.582000000000E+00 + 97 3.000000000000E+00 1.933000000000E+00 + 97 4.000000000000E+00 1.260000000000E+00 + 97 5.000000000000E+00 9.198000000000E-01 + 97 6.000000000000E+00 7.187000000000E-01 + 97 7.000000000000E+00 5.872000000000E-01 + 97 8.000000000000E+00 4.950000000000E-01 + 97 9.000000000000E+00 4.272000000000E-01 + 97 1.000000000000E+01 3.752000000000E-01 + 98 1.000000000000E-03 3.285000000000E+06 + 98 1.131000000000E-03 2.666000000000E+06 + 98 1.279000000000E-03 2.163000000000E+06 + 98 1.279000100000E-03 2.302000000000E+06 + 98 1.500000000000E-03 1.688000000000E+06 + 98 1.616000000000E-03 1.452000000000E+06 + 98 1.616000100000E-03 1.469000000000E+06 + 98 1.705000000000E-03 1.315000000000E+06 + 98 1.799000000000E-03 1.177000000000E+06 + 98 1.799000100000E-03 1.198000000000E+06 + 98 2.000000000000E-03 9.580000000000E+05 + 98 3.000000000000E-03 3.928000000000E+05 + 98 4.000000000000E-03 2.027000000000E+05 + 98 4.253000000000E-03 1.756000000000E+05 + 98 4.253000100000E-03 3.937000000000E+05 + 98 4.373000000000E-03 3.723000000000E+05 + 98 4.497000000000E-03 3.520000000000E+05 + 98 4.497000100000E-03 4.894000000000E+05 + 98 5.000000000000E-03 3.826000000000E+05 + 98 5.109000000000E-03 3.624000000000E+05 + 98 5.109000100000E-03 4.228000000000E+05 + 98 6.000000000000E-03 2.821000000000E+05 + 98 6.359000000000E-03 2.425000000000E+05 + 98 6.359000100000E-03 2.571000000000E+05 + 98 6.554000000000E-03 2.385000000000E+05 + 98 6.754000000000E-03 2.213000000000E+05 + 98 6.754000100000E-03 2.305000000000E+05 + 98 8.000000000000E-03 1.519000000000E+05 + 98 1.000000000000E-02 8.700000000000E+04 + 98 1.500000000000E-02 3.115000000000E+04 + 98 1.993000000000E-02 1.501000000000E+04 + 98 1.993000010000E-02 3.461000000000E+04 + 98 2.000000000000E-02 3.449000000000E+04 + 98 2.525000000000E-02 1.849000000000E+04 + 98 2.525000010000E-02 2.657000000000E+04 + 98 2.568000000000E-02 2.544000000000E+04 + 98 2.611000000000E-02 2.436000000000E+04 + 98 2.611000010000E-02 2.808000000000E+04 + 98 3.000000000000E-02 1.976000000000E+04 + 98 4.000000000000E-02 9.497000000000E+03 + 98 5.000000000000E-02 5.318000000000E+03 + 98 6.000000000000E-02 3.295000000000E+03 + 98 8.000000000000E-02 1.538000000000E+03 + 98 1.000000000000E-01 8.489000000000E+02 + 98 1.360000000000E-01 3.740000000000E+02 + 98 1.360000001000E-01 1.479000000000E+03 + 98 1.500000000000E-01 1.164000000000E+03 + 98 2.000000000000E-01 5.630000000000E+02 + 98 3.000000000000E-01 2.050000000000E+02 + 98 4.000000000000E-01 1.025000000000E+02 + 98 5.000000000000E-01 6.118000000000E+01 + 98 6.000000000000E-01 4.072000000000E+01 + 98 8.000000000000E-01 2.204000000000E+01 + 98 1.000000000000E+00 1.401000000000E+01 + 98 1.022000000000E+00 1.342000000000E+01 + 98 1.250000000000E+00 9.080000000000E+00 + 98 1.500000000000E+00 6.453000000000E+00 + 98 2.000000000000E+00 3.896000000000E+00 + 98 2.044000000000E+00 3.755000000000E+00 + 98 3.000000000000E+00 2.026000000000E+00 + 98 4.000000000000E+00 1.320000000000E+00 + 98 5.000000000000E+00 9.634000000000E-01 + 98 6.000000000000E+00 7.526000000000E-01 + 98 7.000000000000E+00 6.148000000000E-01 + 98 8.000000000000E+00 5.183000000000E-01 + 98 9.000000000000E+00 4.472000000000E-01 + 98 1.000000000000E+01 3.928000000000E-01 + 99 1.000000000000E-03 3.253000000000E+06 + 99 1.016000000000E-03 3.174000000000E+06 + 99 1.032000000000E-03 3.096000000000E+06 + 99 1.032000100000E-03 3.190000000000E+06 + 99 1.168000000000E-03 2.601000000000E+06 + 99 1.321000000000E-03 2.121000000000E+06 + 99 1.321000100000E-03 2.257000000000E+06 + 99 1.500000000000E-03 1.762000000000E+06 + 99 1.680000000000E-03 1.400000000000E+06 + 99 1.680000100000E-03 1.416000000000E+06 + 99 1.772000000000E-03 1.268000000000E+06 + 99 1.868000000000E-03 1.135000000000E+06 + 99 1.868000100000E-03 1.155000000000E+06 + 99 2.000000000000E-03 9.998000000000E+05 + 99 3.000000000000E-03 4.101000000000E+05 + 99 4.000000000000E-03 2.117000000000E+05 + 99 4.374000000000E-03 1.717000000000E+05 + 99 4.374000100000E-03 3.790000000000E+05 + 99 4.500000000000E-03 3.594000000000E+05 + 99 4.630000000000E-03 3.407000000000E+05 + 99 4.630000100000E-03 4.712000000000E+05 + 99 5.000000000000E-03 3.973000000000E+05 + 99 5.252000000000E-03 3.515000000000E+05 + 99 5.252000100000E-03 4.103000000000E+05 + 99 6.000000000000E-03 2.939000000000E+05 + 99 6.574000000000E-03 2.320000000000E+05 + 99 6.574000100000E-03 2.460000000000E+05 + 99 6.773000000000E-03 2.283000000000E+05 + 99 6.977000000000E-03 2.120000000000E+05 + 99 6.977000100000E-03 2.208000000000E+05 + 99 8.000000000000E-03 1.574000000000E+05 + 99 1.000000000000E-02 9.026000000000E+04 + 99 1.500000000000E-02 3.240000000000E+04 + 99 2.000000000000E-02 1.548000000000E+04 + 99 2.041000000000E-02 1.469000000000E+04 + 99 2.041000010000E-02 3.415000000000E+04 + 99 2.304000000000E-02 2.462000000000E+04 + 99 2.602000000000E-02 1.774000000000E+04 + 99 2.602000010000E-02 2.556000000000E+04 + 99 2.646000000000E-02 2.448000000000E+04 + 99 2.690000000000E-02 2.343000000000E+04 + 99 2.690000010000E-02 2.700000000000E+04 + 99 3.000000000000E-02 2.049000000000E+04 + 99 4.000000000000E-02 9.884000000000E+03 + 99 5.000000000000E-02 5.544000000000E+03 + 99 6.000000000000E-02 3.439000000000E+03 + 99 8.000000000000E-02 1.609000000000E+03 + 99 1.000000000000E-01 8.893000000000E+02 + 99 1.395000000000E-01 3.667000000000E+02 + 99 1.395000001000E-01 1.432000000000E+03 + 99 1.500000000000E-01 1.205000000000E+03 + 99 2.000000000000E-01 5.837000000000E+02 + 99 3.000000000000E-01 2.132000000000E+02 + 99 4.000000000000E-01 1.069000000000E+02 + 99 5.000000000000E-01 6.389000000000E+01 + 99 6.000000000000E-01 4.258000000000E+01 + 99 8.000000000000E-01 2.308000000000E+01 + 99 1.000000000000E+00 1.467000000000E+01 + 99 1.022000000000E+00 1.406000000000E+01 + 99 1.250000000000E+00 9.516000000000E+00 + 99 1.500000000000E+00 6.763000000000E+00 + 99 2.000000000000E+00 4.082000000000E+00 + 99 2.044000000000E+00 3.935000000000E+00 + 99 3.000000000000E+00 2.122000000000E+00 + 99 4.000000000000E+00 1.382000000000E+00 + 99 5.000000000000E+00 1.009000000000E+00 + 99 6.000000000000E+00 7.879000000000E-01 + 99 7.000000000000E+00 6.436000000000E-01 + 99 8.000000000000E+00 5.424000000000E-01 + 99 9.000000000000E+00 4.680000000000E-01 + 99 1.000000000000E+01 4.110000000000E-01 +100 1.000000000000E-03 3.039000000000E+06 +100 1.001000000000E-03 3.034000000000E+06 +100 1.002000000000E-03 3.029000000000E+06 +100 1.002000100000E-03 3.160000000000E+06 +100 1.035000000000E-03 3.094000000000E+06 +100 1.069000000000E-03 3.030000000000E+06 +100 1.069000100000E-03 3.119000000000E+06 +100 1.208000000000E-03 2.543000000000E+06 +100 1.366000000000E-03 2.073000000000E+06 +100 1.366000100000E-03 2.206000000000E+06 +100 1.500000000000E-03 1.838000000000E+06 +100 1.747000000000E-03 1.347000000000E+06 +100 1.747000100000E-03 1.363000000000E+06 +100 1.840000000000E-03 1.223000000000E+06 +100 1.937000000000E-03 1.097000000000E+06 +100 1.937000100000E-03 1.117000000000E+06 +100 2.000000000000E-03 1.043000000000E+06 +100 3.000000000000E-03 4.279000000000E+05 +100 4.000000000000E-03 2.209000000000E+05 +100 4.498000000000E-03 1.677000000000E+05 +100 4.498000100000E-03 3.651000000000E+05 +100 4.630000000000E-03 3.470000000000E+05 +100 4.766000000000E-03 3.298000000000E+05 +100 4.766000100000E-03 4.534000000000E+05 +100 5.000000000000E-03 4.128000000000E+05 +100 5.397000000000E-03 3.413000000000E+05 +100 5.397000100000E-03 3.986000000000E+05 +100 6.000000000000E-03 3.057000000000E+05 +100 6.793000000000E-03 2.220000000000E+05 +100 6.793000100000E-03 2.353000000000E+05 +100 6.996000000000E-03 2.185000000000E+05 +100 7.205000000000E-03 2.030000000000E+05 +100 7.205000100000E-03 2.114000000000E+05 +100 8.000000000000E-03 1.631000000000E+05 +100 1.000000000000E-02 9.366000000000E+04 +100 1.500000000000E-02 3.367000000000E+04 +100 2.000000000000E-02 1.611000000000E+04 +100 2.090000000000E-02 1.438000000000E+04 +100 2.090000010000E-02 3.330000000000E+04 +100 2.367000000000E-02 2.382000000000E+04 +100 2.681000000000E-02 1.703000000000E+04 +100 2.681000010000E-02 2.459000000000E+04 +100 2.725000000000E-02 2.356000000000E+04 +100 2.770000000000E-02 2.258000000000E+04 +100 2.770000010000E-02 2.601000000000E+04 +100 3.000000000000E-02 2.128000000000E+04 +100 4.000000000000E-02 1.028000000000E+04 +100 5.000000000000E-02 5.775000000000E+03 +100 6.000000000000E-02 3.588000000000E+03 +100 8.000000000000E-02 1.682000000000E+03 +100 1.000000000000E-01 9.311000000000E+02 +100 1.431000000000E-01 3.597000000000E+02 +100 1.431000001000E-01 1.387000000000E+03 +100 1.500000000000E-01 1.240000000000E+03 +100 2.000000000000E-01 6.049000000000E+02 +100 3.000000000000E-01 2.216000000000E+02 +100 4.000000000000E-01 1.114000000000E+02 +100 5.000000000000E-01 6.668000000000E+01 +100 6.000000000000E-01 4.450000000000E+01 +100 8.000000000000E-01 2.415000000000E+01 +100 1.000000000000E+00 1.537000000000E+01 +100 1.022000000000E+00 1.472000000000E+01 +100 1.250000000000E+00 9.970000000000E+00 +100 1.500000000000E+00 7.085000000000E+00 +100 2.000000000000E+00 4.276000000000E+00 +100 2.044000000000E+00 4.122000000000E+00 +100 3.000000000000E+00 2.222000000000E+00 +100 4.000000000000E+00 1.447000000000E+00 +100 5.000000000000E+00 1.056000000000E+00 +100 6.000000000000E+00 8.246000000000E-01 +100 7.000000000000E+00 6.734000000000E-01 +100 8.000000000000E+00 5.675000000000E-01 +100 9.000000000000E+00 4.895000000000E-01 +100 1.000000000000E+01 4.299000000000E-01 diff --git a/decay.cc b/decay.cc index b2f4e3a46..13c68584c 100644 --- a/decay.cc +++ b/decay.cc @@ -1,12 +1,21 @@ #include "decay.h" +#ifdef MPI_ON +#include +#endif + #include #include +#include #include +#include +#include +#include #include #include #include -#include +#include +#include #include #include #include @@ -14,18 +23,22 @@ #include #include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "gammapkt.h" #include "globals.h" #include "grid.h" #include "input.h" #include "nonthermal.h" +#include "packet.h" #include "sn3d.h" namespace decay { -constexpr int Z_MAX = 118; -constexpr std::string_view elsymbols[Z_MAX + 1] = { +namespace { + +constexpr std::array elsymbols{ "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,21 +46,24 @@ constexpr std::string_view elsymbols[Z_MAX + 1] = { "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Uut", "Fl", "Uup", "Lv", "Uus", "Uuo"}; - -struct nuclide { - 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] +constexpr int Z_MAX = elsymbols.size() - 1; + +struct Nuclide { + 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; +std::vector nuclides; + +} // anonymous namespace 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) @@ -101,33 +117,34 @@ constexpr auto decay_daughter_a(const int /*z_parent*/, const int a_parent, cons // 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 { - 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() +struct DecayPath { + 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{ + 0.}; // 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; +std::vector decaypaths; // decaypath_energy_per_mass points to an array of length npts_model * num_decaypaths // the index [mgi * num_decaypaths + i] will hold the decay energy per mass [erg/g] released by chain i in cell mgi // during the simulation time range -double *decaypath_energy_per_mass = nullptr; +double *decaypath_energy_per_mass{}; #ifdef MPI_ON -MPI_Win win_decaypath_energy_per_mass = MPI_WIN_NULL; +MPI_Win win_decaypath_energy_per_mass{MPI_WIN_NULL}; #endif -auto get_num_nuclides() -> int { return nuclides.size(); } +auto get_num_nuclides() -> int { return static_cast(nuclides.size()); } -auto get_elname(const int z) -> const char * { +auto get_elname(const int z) -> std::string { assert_testmodeonly(z <= Z_MAX); - return &elsymbols[z].front(); + return std::string(elsymbols[z]); } auto get_nuc_z(int nucindex) -> int { @@ -185,7 +202,7 @@ static auto get_meanlife(int nucindex) -> double { return nuclides[nucindex].meanlife; } -static void printout_nuclidename(const int z, const int a) { printout("(Z=%d)%s%d", z, get_elname(z), a); } +static void printout_nuclidename(const int z, const int a) { printout("(Z=%d)%s%d", z, get_elname(z).c_str(), a); } static void printout_nuclidemeanlife(const int z, const int a) { const int nucindex = get_nucindex_or_neg_one(z, a); @@ -287,28 +304,23 @@ static auto nucdecayenergyqval(int nucindex, int decaytype) -> double { static auto get_num_decaypaths() -> int { return static_cast(decaypaths.size()); } -static auto get_decaypathlength(const decaypath &dpath) -> int { return static_cast(dpath.z.size()); } +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(const decaypath &decaypath) -> double -// return the product of all branching factors in the decay path -{ +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(decaypath); i++) { + const auto decaypathlength = get_decaypathlength(decaypath); + for (int i = 0; i < decaypathlength; i++) { branchprod = branchprod * get_nuc_decaybranchprob(decaypath.nucindex[i], decaypath.decaytypes[i]); } return branchprod; } -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 -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 = dpath.nucindex.back(); const int decaytype_end = dpath.decaytypes.back(); return nucdecayenergy(nucindex_end, decaytype_end); @@ -374,7 +386,7 @@ static void extend_lastdecaypath() // follow decays at the ends of the current list of decaypaths, // to get decaypaths from all descendants { - const int startdecaypathindex = decaypaths.size() - 1; + const int startdecaypathindex = static_cast(decaypaths.size() - 1); const int daughter_z = decaypaths[startdecaypathindex].final_daughter_z(); const int daughter_a = decaypaths[startdecaypathindex].final_daughter_a(); @@ -389,24 +401,23 @@ static void extend_lastdecaypath() for (int i = 0; i < get_decaypathlength(startdecaypathindex); i++) { if (decaypaths[startdecaypathindex].z[i] == daughter_z && decaypaths[startdecaypathindex].a[i] == daughter_a) { printout("\nERROR: Loop found in nuclear decay chain.\n"); - abort(); + std::abort(); } } decaypaths.push_back(decaypaths[startdecaypathindex]); - const int lastindex = decaypaths.size() - 1; - decaypaths[lastindex].z.push_back(daughter_z); - decaypaths[lastindex].a.push_back(daughter_a); - decaypaths[lastindex].nucindex.push_back(daughter_nucindex); - decaypaths[lastindex].decaytypes.push_back(dectypeindex2); + decaypaths.back().z.push_back(daughter_z); + decaypaths.back().a.push_back(daughter_a); + decaypaths.back().nucindex.push_back(daughter_nucindex); + decaypaths.back().decaytypes.push_back(dectypeindex2); extend_lastdecaypath(); } } } -static auto operator<(const struct decaypath &d1, const struct decaypath &d2) -> bool +static auto operator<(const DecayPath &d1, const DecayPath &d2) -> bool // true if d1 < d2 // chains are sorted by mass number, then atomic number, then length { @@ -432,7 +443,7 @@ static auto operator<(const struct decaypath &d1, const struct decaypath &d2) -> } static void find_decaypaths(const std::vector &custom_zlist, const std::vector &custom_alist, - std::vector &standard_nuclides) { + std::vector &standard_nuclides) { decaypaths.clear(); for (int startnucindex = 0; startnucindex < get_num_nuclides(); startnucindex++) { const int z = get_nuc_z(startnucindex); @@ -491,7 +502,7 @@ static void find_decaypaths(const std::vector &custom_zlist, const std::vec } static void filter_unused_nuclides(const std::vector &custom_zlist, const std::vector &custom_alist, - std::vector &standard_nuclides) { + 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(), @@ -509,20 +520,26 @@ static void filter_unused_nuclides(const std::vector &custom_zlist, const s } } - // 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; - } - + const bool in_any_decaypath = std::ranges::any_of(decaypaths, [&nuc](const auto &decaypath) { 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; + // nuc is in the decay path + return true; }; - } + }; + if (decaypath.final_daughter_z() == nuc.z && decaypath.final_daughter_a() == nuc.a) { + // nuc is the final daughter of a decay path + return true; + }; + + return false; + }); + + if (in_any_decaypath) { + return false; } - printout("removing unused nuclide (Z=%d)%s-%d\n", nuc.z, get_elname(nuc.z), nuc.a); + + printout("removing unused nuclide (Z=%d)%s%d\n", nuc.z, get_elname(nuc.z).c_str(), nuc.a); return true; }), nuclides.end()); @@ -542,8 +559,7 @@ auto get_nucstring_z(const std::string &strnuc) -> int elcode.erase(std::remove_if(elcode.begin(), elcode.end(), &isdigit), elcode.end()); for (int z = 0; z <= Z_MAX; z++) { - if (strcmp(elcode.c_str(), get_elname(z)) == 0) // first to letters match el symbol - { + if (elcode == get_elname(z)) { return z; } } @@ -653,7 +669,7 @@ void init_nuclides(const std::vector &custom_zlist, const std::vector 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", + // 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.); } @@ -678,13 +694,13 @@ void init_nuclides(const std::vector &custom_zlist, const std::vector const bool keeprow = ((branch_alpha > 0. || branch_beta > 0.) && halflife > 0.); if (keeprow) { - const double tau_sec = halflife / log(2); + const double tau_sec = halflife / std::numbers::ln2; int alphanucindex = -1; if (nuc_exists(z, a)) { alphanucindex = get_nucindex(z, a); } else { nuclides.push_back({.z = z, .a = a, .meanlife = tau_sec, .endecay_gamma = e_gamma_mev * MEV}); - alphanucindex = nuclides.size() - 1; + alphanucindex = static_cast(nuclides.size() - 1); } nuclides[alphanucindex].endecay_alpha = e_alpha_mev * MEV; nuclides[alphanucindex].branchprobs[DECAYTYPE_BETAMINUS] = branch_beta; @@ -692,7 +708,7 @@ void init_nuclides(const std::vector &custom_zlist, const std::vector nuclides[alphanucindex].branchprobs[DECAYTYPE_ALPHA] = branch_alpha; nuclides[alphanucindex].endecay_q[DECAYTYPE_ALPHA] = Q_total_alphadec * MEV; - // printout("alphadecay file: Adding (Z=%d)%s-%d endecay_alpha %g endecay_gamma %g tau_s %g\n", + // printout("alphadecay file: Adding (Z=%d)%s%d endecay_alpha %g endecay_gamma %g tau_s %g\n", // z, get_elname(z), a, e_alpha_mev, e_gamma_mev, tau_sec); } } @@ -721,7 +737,7 @@ void init_nuclides(const std::vector &custom_zlist, const std::vector 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(); + gammapkt::init_gamma_data(); // TODO: generalise this to all included nuclides printout("decayenergy(NI56), decayenergy(CO56), decayenergy_gamma(CO56): %g, %g, %g\n", @@ -741,7 +757,8 @@ static auto sample_decaytime(const int decaypathindex, const double tdecaymin, c while (tdecay <= tdecaymin || tdecay >= tdecaymax) { tdecay = t_model; // can't decay before initial model snapshot time - for (int i = 0; i < get_decaypathlength(decaypathindex); i++) { + const auto decaypathlength = get_decaypathlength(decaypathindex); + for (int i = 0; i < decaypathlength; i++) { const int nucindex = decaypaths[decaypathindex].nucindex[i]; const double zrand = rng_uniform_pos(); tdecay += -get_meanlife(nucindex) * log(zrand); @@ -1009,7 +1026,7 @@ static auto calculate_simtime_endecay_per_ejectamass(const int mgi, const int de static auto get_simtime_endecay_per_ejectamass(const int mgi, const int decaypathindex) -> double // get the decay energy released during the simulation time per unit mass [erg/g] { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(mgi); + const size_t nonemptymgi = grid::get_modelcell_nonemptymgi(mgi); const double chainendecay = decaypath_energy_per_mass[nonemptymgi * get_num_decaypaths() + decaypathindex]; assert_testmodeonly(chainendecay >= 0.); assert_testmodeonly(std::isfinite(chainendecay)); @@ -1070,7 +1087,7 @@ void setup_decaypath_energy_per_mass() { "shared)...", nonempty_npts_model * get_num_decaypaths() * sizeof(double) / 1024. / 1024.); #ifdef MPI_ON - int my_rank_cells = nonempty_npts_model / globals::node_nprocs; + size_t my_rank_cells = nonempty_npts_model / globals::node_nprocs; // rank_in_node 0 gets any remainder if (globals::rank_in_node == 0) { my_rank_cells += nonempty_npts_model - (my_rank_cells * globals::node_nprocs); @@ -1093,11 +1110,11 @@ void setup_decaypath_energy_per_mass() { #endif printout("Calculating decaypath_energy_per_mass for all cells..."); - const int num_decaypaths = get_num_decaypaths(); + const size_t num_decaypaths = get_num_decaypaths(); for (int nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { if (nonemptymgi % globals::node_nprocs == globals::rank_in_node) { const int mgi = grid::get_mgi_of_nonemptymgi(nonemptymgi); - for (int decaypathindex = 0; decaypathindex < num_decaypaths; decaypathindex++) { + for (size_t decaypathindex = 0; decaypathindex < num_decaypaths; decaypathindex++) { decaypath_energy_per_mass[nonemptymgi * num_decaypaths + decaypathindex] = calculate_simtime_endecay_per_ejectamass(mgi, decaypathindex); } @@ -1130,7 +1147,8 @@ auto get_particle_injection_rate(const int modelgridindex, const double t, const // energy release rate in form of kinetic energy of positrons, electrons, and alpha particles in [erg/s/g] { double dep_sum = 0.; - for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { + const auto num_nuclides = get_num_nuclides(); + for (int nucindex = 0; nucindex < num_nuclides; nucindex++) { const int z = get_nuc_z(nucindex); const int a = get_nuc_a(nucindex); const double meanlife = get_meanlife(nucindex); @@ -1155,7 +1173,8 @@ auto get_qdot_modelcell(const int modelgridindex, const double t, const int deca // energy release rate [erg/s/g] including everything (even neutrinos that are ignored elsewhere) { double qdot = 0.; - for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { + const auto num_nuclides = get_num_nuclides(); + for (int nucindex = 0; nucindex < num_nuclides; nucindex++) { const int z = get_nuc_z(nucindex); const int a = get_nuc_a(nucindex); const double meanlife = get_meanlife(nucindex); @@ -1176,7 +1195,8 @@ auto get_qdot_modelcell(const int modelgridindex, const double t, const int deca auto get_global_etot_t0_tinf() -> double { double etot_tinf = 0.; - for (int decaypathindex = 0; decaypathindex < get_num_decaypaths(); decaypathindex++) { + const auto num_decaypaths = get_num_decaypaths(); + for (int decaypathindex = 0; decaypathindex < num_decaypaths; decaypathindex++) { const int z_top = decaypaths[decaypathindex].z[0]; const int a_top = decaypaths[decaypathindex].a[0]; @@ -1195,12 +1215,13 @@ void update_abundances(const int modelgridindex, const int timestep, const doubl for (int element = get_nelements() - 1; element >= 0; element--) { const int atomic_number = get_atomicnumber(element); - std::set a_isotopes; - // for the current element, - // the mass fraction sum of radioactive isotopes, and stable nuclei coming from other decays + std::set a_isotopes; // track which isotopes have been added to the sum to avoid double counting + + // the mass fraction sum of radioactive isotopes, and stable nuclei coming from other decays for the current element double isomassfracsum = 0.; double isomassfrac_on_nucmass_sum = 0.; - for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { + const auto num_nuclides = get_num_nuclides(); + for (int nucindex = 0; nucindex < num_nuclides; nucindex++) { const int nuc_z = get_nuc_z(nucindex); const int a = get_nuc_a(nucindex); if (nuc_z == atomic_number) { @@ -1244,12 +1265,18 @@ void update_abundances(const int modelgridindex, const int timestep, const doubl isomassfrac_on_nucmass_sum += stable_init_massfrac / globals::elements[element].initstablemeannucmass; grid::set_elem_abundance(modelgridindex, element, isomassfracsum); - grid::set_element_meanweight(modelgridindex, element, isomassfracsum / isomassfrac_on_nucmass_sum); + if (isomassfrac_on_nucmass_sum > 0.) { + grid::set_element_meanweight(modelgridindex, element, isomassfracsum / isomassfrac_on_nucmass_sum); + } else { + // avoid a divide by zero + grid::set_element_meanweight(modelgridindex, element, globals::elements[element].initstablemeannucmass); + } } - // total number of electrons in grid cell which are possible targets for compton scattering of gamma rays + // 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 auto nelements = get_nelements(); + for (int element = 0; element < nelements; element++) { const double nnelement = grid::get_elem_numberdens(modelgridindex, element); nnetot += nnelement * get_atomicnumber(element); } @@ -1296,7 +1323,8 @@ void fprint_nuc_abundances(FILE *estimators_file, const int modelgridindex, cons const int atomic_number = get_atomicnumber(element); std::set a_isotopes; // ensure we don't repeat isotopes - for (int nucindex = 0; nucindex < get_num_nuclides(); nucindex++) { + const auto num_nuclides = get_num_nuclides(); + for (int nucindex = 0; nucindex < num_nuclides; nucindex++) { const int nuc_z = get_nuc_z(nucindex); const int nuc_a = get_nuc_a(nucindex); if (nuc_z == atomic_number) { // isotope of this element is on the network @@ -1307,7 +1335,7 @@ void fprint_nuc_abundances(FILE *estimators_file, const int modelgridindex, cons if (massfrac > 0) { const double numberdens = massfrac / nucmass(atomic_number, nuc_a) * rho; - fprintf(estimators_file, " %s%d: %9.3e", get_elname(atomic_number), nuc_a, numberdens); + fprintf(estimators_file, " %s%d: %9.3e", get_elname(atomic_number).c_str(), nuc_a, numberdens); } } } else { // not the element that we want, but check if a decay produces it @@ -1323,7 +1351,7 @@ void fprint_nuc_abundances(FILE *estimators_file, const int modelgridindex, cons // stable const double massfrac = get_nuc_massfrac(modelgridindex, atomic_number, nuc_a, t_current); const double numberdens = massfrac / nucmass(nuc_z, nuc_a) * rho; - fprintf(estimators_file, " %s%d: %9.3e", get_elname(atomic_number), nuc_a, numberdens); + fprintf(estimators_file, " %s%d: %9.3e", get_elname(atomic_number).c_str(), nuc_a, numberdens); } } } @@ -1335,18 +1363,18 @@ void fprint_nuc_abundances(FILE *estimators_file, const int modelgridindex, cons if (otherstablemassfrac > 0) { const double meannucmass = globals::elements[element].initstablemeannucmass; const double otherstable_numberdens = otherstablemassfrac / meannucmass * grid::get_rho(modelgridindex); - fprintf(estimators_file, " %s_otherstable: %9.3e", get_elname(atomic_number), otherstable_numberdens); + fprintf(estimators_file, " %s_otherstable: %9.3e", get_elname(atomic_number).c_str(), otherstable_numberdens); } fprintf(estimators_file, "\n"); } -void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt_ptr) { +void setup_radioactive_pellet(const double e0, const int mgi, Packet &pkt) { const int num_decaypaths = get_num_decaypaths(); // decay channels include all radioactive decay paths, and possibly also an initial cell energy channel const int num_decaychannels = num_decaypaths + ((INITIAL_PACKETS_ON && USE_MODEL_INITIAL_ENERGY) ? 1 : 0); - auto cumulative_en_sum = std::make_unique(num_decaychannels); + auto cumulative_en_sum = std::vector(num_decaychannels, 0.); double energysum = 0.; // add the radioactive decay paths @@ -1366,10 +1394,9 @@ void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt const double zrand_en = rng_uniform() * cumulative_en_sum[num_decaychannels - 1]; - // first cumulative_en_sum[i] such that cumulative_en_sum[i] > zrand_en - const double *const upperval = - std::upper_bound(&cumulative_en_sum[0], &cumulative_en_sum[num_decaychannels], zrand_en); - const ptrdiff_t decaychannelindex = upperval - &cumulative_en_sum[0]; + // first decaychannelindex such that cumulative_en_sum[decaychannelindex] > zrand_en + const int decaychannelindex = std::distance( + cumulative_en_sum.cbegin(), std::upper_bound(cumulative_en_sum.cbegin(), cumulative_en_sum.cend(), zrand_en)); assert_always(decaychannelindex >= 0); assert_always(decaychannelindex < num_decaychannels); @@ -1380,13 +1407,13 @@ void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt assert_always(USE_MODEL_INITIAL_ENERGY); assert_always(INITIAL_PACKETS_ON); - pkt_ptr->prop_time = globals::tmin; - pkt_ptr->tdecay = globals::tmin; - pkt_ptr->type = TYPE_RADIOACTIVE_PELLET; - pkt_ptr->e_cmf = e0; - pkt_ptr->nu_cmf = e0 / H; - pkt_ptr->pellet_nucindex = -1; - pkt_ptr->pellet_decaytype = -1; + pkt.prop_time = globals::tmin; + pkt.tdecay = globals::tmin; + pkt.type = TYPE_RADIOACTIVE_PELLET; + pkt.e_cmf = e0; + pkt.nu_cmf = e0 / H; + pkt.pellet_nucindex = -1; + pkt.pellet_decaytype = -1; return; } @@ -1396,14 +1423,14 @@ void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt const double tdecaymin = !INITIAL_PACKETS_ON ? globals::tmin : grid::get_t_model(); if constexpr (UNIFORM_PELLET_ENERGIES) { - pkt_ptr->tdecay = sample_decaytime(decaypathindex, tdecaymin, globals::tmax); - pkt_ptr->e_cmf = e0; + pkt.tdecay = sample_decaytime(decaypathindex, tdecaymin, globals::tmax); + pkt.e_cmf = e0; } else { // use uniform decay time distribution and scale the packet energies instead. // keeping the pellet decay rate constant will give better statistics at late times // when very little energy and few packets are released const double zrand = rng_uniform(); - pkt_ptr->tdecay = zrand * tdecaymin + (1. - zrand) * globals::tmax; + pkt.tdecay = zrand * tdecaymin + (1. - zrand) * globals::tmax; // we need to scale the packet energy up or down according to decay rate at the randomly selected time. // e0 is the average energy per packet for this cell and decaypath, so we scale this up or down @@ -1411,9 +1438,9 @@ void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt const double avgpower = get_simtime_endecay_per_ejectamass(mgi, decaypathindex) / (globals::tmax - tdecaymin); assert_always(avgpower > 0.); assert_always(std::isfinite(avgpower)); - pkt_ptr->e_cmf = e0 * get_decaypath_power_per_ejectamass(decaypathindex, mgi, pkt_ptr->tdecay) / avgpower; - assert_always(pkt_ptr->e_cmf >= 0); - assert_always(std::isfinite(pkt_ptr->e_cmf)); + pkt.e_cmf = e0 * get_decaypath_power_per_ejectamass(decaypathindex, mgi, pkt.tdecay) / avgpower; + assert_always(pkt.e_cmf >= 0); + assert_always(std::isfinite(pkt.e_cmf)); } // final decaying nuclide at the end of the chain @@ -1421,16 +1448,15 @@ void setup_radioactive_pellet(const double e0, const int mgi, struct packet *pkt const int nucindex = decaypaths[decaypathindex].nucindex[pathlength - 1]; const int decaytype = decaypaths[decaypathindex].decaytypes[pathlength - 1]; - pkt_ptr->type = TYPE_RADIOACTIVE_PELLET; - pkt_ptr->pellet_nucindex = nucindex; - pkt_ptr->pellet_decaytype = decaytype; + pkt.type = TYPE_RADIOACTIVE_PELLET; + pkt.pellet_nucindex = nucindex; + pkt.pellet_decaytype = decaytype; const auto engamma = nucdecayenergygamma(nucindex); const auto enparticle = nucdecayenergyparticle(nucindex, decaytype); - const double zrand = rng_uniform(); - pkt_ptr->originated_from_particlenotgamma = (zrand >= engamma / (engamma + enparticle)); - pkt_ptr->nu_cmf = enparticle / H; // will be overwritten for gamma rays, but affects the thermalisation of particles + pkt.originated_from_particlenotgamma = (rng_uniform() >= engamma / (engamma + enparticle)); + pkt.nu_cmf = enparticle / H; // will be overwritten for gamma rays, but affects the thermalisation of particles } void cleanup() { free_decaypath_energy_per_mass(); } diff --git a/decay.h b/decay.h index 1ba263f6d..2f7b89c23 100644 --- a/decay.h +++ b/decay.h @@ -1,3 +1,4 @@ +#pragma once #ifndef DECAY_H #define DECAY_H @@ -25,30 +26,30 @@ constexpr std::array all_decaytypes = { decaytypes::DECAYTYPE_BETAMINUS, decaytypes::DECAYTYPE_NONE}; void init_nuclides(const std::vector &zlist, const std::vector &alist); -auto get_nucstring_z(const std::string &strnuc) -> int; -auto get_nucstring_a(const std::string &strnuc) -> int; -auto get_num_nuclides() -> int; -auto get_elname(int z) -> const char *; -auto get_nuc_z(int nucindex) -> int; -auto get_nuc_a(int nucindex) -> int; -auto get_nucindex(int z, int a) -> int; -auto nuc_exists(int z, int a) -> bool; -auto nucdecayenergygamma(int nucindex) -> double; -auto nucdecayenergygamma(int z, int a) -> double; +[[nodiscard]] auto get_nucstring_z(const std::string &strnuc) -> int; +[[nodiscard]] auto get_nucstring_a(const std::string &strnuc) -> int; +[[nodiscard]] auto get_num_nuclides() -> int; +[[nodiscard]] auto get_elname(int z) -> std::string; +[[nodiscard]] auto get_nuc_z(int nucindex) -> int; +[[nodiscard]] auto get_nuc_a(int nucindex) -> int; +[[nodiscard]] auto get_nucindex(int z, int a) -> int; +[[nodiscard]] auto nuc_exists(int z, int a) -> bool; +[[nodiscard]] auto nucdecayenergygamma(int nucindex) -> double; +[[nodiscard]] auto nucdecayenergygamma(int z, int a) -> double; void set_nucdecayenergygamma(int nucindex, double value); void update_abundances(int modelgridindex, int timestep, double t_current); -auto get_endecay_per_ejectamass_t0_to_time_withexpansion(int modelgridindex, double tstart) -> double; -auto get_modelcell_simtime_endecay_per_mass(int mgi) -> double; +[[nodiscard]] auto get_endecay_per_ejectamass_t0_to_time_withexpansion(int modelgridindex, double tstart) -> double; +[[nodiscard]] auto get_modelcell_simtime_endecay_per_mass(int mgi) -> double; void setup_decaypath_energy_per_mass(); void free_decaypath_energy_per_mass(); -auto get_qdot_modelcell(int modelgridindex, double t, int decaytype) -> double; -auto get_particle_injection_rate(int modelgridindex, double t, int decaytype) -> double; -auto get_global_etot_t0_tinf() -> double; +[[nodiscard]] auto get_qdot_modelcell(int modelgridindex, double t, int decaytype) -> double; +[[nodiscard]] auto get_particle_injection_rate(int modelgridindex, double t, int decaytype) -> double; +[[nodiscard]] auto get_global_etot_t0_tinf() -> double; 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 setup_radioactive_pellet(double e0, int mgi, Packet &pkt); void cleanup(); -auto constexpr nucmass(int z, int a) -> double { return a * MH; } +[[nodiscard]] 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 fabbb4315..48b1bd97a 100644 --- a/exspec.cc +++ b/exspec.cc @@ -1,29 +1,38 @@ #include "exspec.h" +#include #include #include +#include +#include #include -#include +#include +#ifdef MPI_ON +#include +#endif +#include +#include +#include "artisoptions.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "input.h" -#include "light_curve.h" +#include "packet.h" #include "sn3d.h" -#include "spectrum.h" +#include "spectrum_lightcurve.h" #include "version.h" -// threadprivate variables -FILE *output_file = nullptr; -int tid = 0; -bool use_cellhist = false; -std::mt19937 stdrng(std::random_device{}()); -gsl_integration_workspace *gslworkspace = nullptr; +std::mt19937 stdrng{std::random_device{}()}; + +std::ofstream output_file; + +namespace { -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) { +void do_angle_bin(const int a, Packet *pkts, bool load_allrank_packets, Spectra &rpkt_spectra, Spectra &stokes_i, + Spectra &stokes_q, Spectra &stokes_u, Spectra &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.); @@ -43,7 +52,7 @@ static void do_angle_bin(const int a, packet *pkts, bool load_allrank_packets, s 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; + Packet *pkts_start = load_allrank_packets ? &pkts[p * globals::npkts] : pkts; if (a == -1 || !load_allrank_packets) { char pktfilename[MAXFILENAMELENGTH]; @@ -77,14 +86,14 @@ static void do_angle_bin(const int a, packet *pkts, bool load_allrank_packets, s 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, + 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); + 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); } } } @@ -150,8 +159,10 @@ static void do_angle_bin(const int a, packet *pkts, bool load_allrank_packets, s } } -auto main(int argc, char *argv[]) -> int { - const time_t sys_time_start = time(nullptr); +} // anonymous namespace + +auto main(int argc, char *argv[]) -> int { // NOLINT(misc-unused-parameters) + const auto sys_time_start = std::time(nullptr); #ifdef MPI_ON MPI_Init(&argc, &argv); @@ -159,23 +170,13 @@ auto main(int argc, char *argv[]) -> int { globals::setup_mpi_vars(); - globals::startofline = std::make_unique(get_max_threads()); - if (globals::rank_global == 0) { - check_already_running(); - } - - // make sure rank 0 checked for a pid file before we proceed -#ifdef MPI_ON - MPI_Barrier(MPI_COMM_WORLD); -#endif + check_already_running(); 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; + output_file = std::ofstream(filename); + assert_always(output_file.is_open()); } printout("git branch %s\n", GIT_BRANCH); @@ -184,7 +185,6 @@ auto main(int argc, char *argv[]) -> int { printout("git status %s\n", GIT_STATUS); - // printout("Hash of most recent commit: %s\n",GIT_HASH); printout("exspec compiled at %s on %s\n", __TIME__, __DATE__); #if defined TESTMODE && TESTMODE @@ -195,9 +195,8 @@ auto main(int argc, char *argv[]) -> int { printout("process id (pid): %d\n", getpid()); printout("MPI enabled:\n"); printout(" rank %d of [0..%d] in MPI_COMM_WORLD\n", globals::rank_global, globals::nprocs - 1); - printout(" node %d of [0..%d]\n", globals::node_id, globals::node_count - 1); - printout(" rank %d of [0..%d] within this node (MPI_COMM_WORLD_SHARED)\n", globals::rank_in_node, - globals::node_nprocs - 1); + printout(" rank %d of [0..%d] in MPI_COMM_WORLD_SHARED on node %d of [0..%d]\n", globals::rank_in_node, + globals::node_nprocs - 1, globals::node_id, globals::node_count - 1); #else printout("MPI is disabled in this build\n"); #endif @@ -209,38 +208,38 @@ auto main(int argc, char *argv[]) -> int { printout("Begining exspec.\n"); /// Get input stuff - printout("time before input %ld\n", time(nullptr)); + printout("time before input %ld\n", std::time(nullptr)); input(globals::rank_global); - printout("time after input %ld\n", time(nullptr)); + printout("time after input %ld\n", std::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 - auto *pkts = static_cast(malloc(globals::nprocs_exspec * globals::npkts * sizeof(struct packet))); + auto *pkts = static_cast(malloc(globals::nprocs_exspec * globals::npkts * sizeof(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.); + globals::nprocs_exspec * globals::npkts * sizeof(Packet) / 1024. / 1024.); } else { printout("mem_usage: malloc failed to allocate memory for all packets\n"); printout( "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.); - pkts = static_cast(malloc(globals::npkts * sizeof(struct packet))); + globals::nprocs_exspec * globals::npkts * sizeof(Packet) / 1024. / 1024.); + pkts = static_cast(malloc(globals::npkts * sizeof(Packet))); assert_always(pkts != nullptr); } init_spectrum_trace(); // needed for TRACE_EMISSION_ABSORPTION_REGION_ON - struct spec rpkt_spectra; + Spectra rpkt_spectra; - struct spec stokes_i; - struct spec stokes_q; - struct spec stokes_u; + Spectra stokes_i; + Spectra stokes_q; + Spectra stokes_u; - struct spec gamma_spectra; + Spectra gamma_spectra; time_init(); @@ -252,11 +251,7 @@ auto main(int argc, char *argv[]) -> int { free(pkts); decay::cleanup(); - printout("exspec finished at %ld (tstart + %ld seconds)\n", time(nullptr), time(nullptr) - sys_time_start); - - if (output_file != nullptr) { - fclose(output_file); - } + printout("exspec finished at %ld (tstart + %ld seconds)\n", std::time(nullptr), std::time(nullptr) - sys_time_start); #ifdef MPI_ON MPI_Finalize(); diff --git a/exspec.h b/exspec.h index a59c720db..a1405f8df 100644 --- a/exspec.h +++ b/exspec.h @@ -1,3 +1,4 @@ +#pragma once #ifndef EXSPEC_H #define EXSPEC_H diff --git a/gammapkt.cc b/gammapkt.cc index 3798d6adf..f6f59fe32 100644 --- a/gammapkt.cc +++ b/gammapkt.cc @@ -1,13 +1,24 @@ #include "gammapkt.h" #include +#include +#include #include -#include +#include +#include +#include +#include #include -#include +#include +#include +#include #include +#include "artisoptions.h" +#include "atomic.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "packet.h" #include "sn3d.h" @@ -17,20 +28,28 @@ namespace gammapkt { // Code for handing gamma rays - creation and propagation -struct gamma_spec { - std::unique_ptr energy = nullptr; // in erg - std::unique_ptr probability = nullptr; - int nlines = 0; +struct GammaLine { + double energy{}; // in erg + double probability{}; +}; + +static std::vector> gamma_spectra; + +struct el_photoion_data { + double energy; // energy in MeV + double sigma_xcom; // cross section in barns/atom }; -static struct gamma_spec *gamma_spectra; +static constexpr int numb_xcom_elements = USE_XCOM_GAMMAPHOTOION ? 100 : 0; + +static std::array, numb_xcom_elements> photoion_data; struct gammaline { int nucindex; // is it a Ni56, Co56, a fake line, etc int nucgammaindex; // which of the lines of that nuclide is it double energy; // in erg - auto operator<(const struct gammaline &g2) const -> bool { + auto operator<(const gammaline &g2) const -> bool { // true if d1 < d2 if (energy < g2.energy) { return true; @@ -45,7 +64,7 @@ struct gammaline { } }; -static std::vector allnuc_gamma_line_list; +static std::vector allnuc_gamma_line_list; static void read_gamma_spectrum(const int nucindex, const char filename[50]) // reads in gamma_spectra and returns the average energy in gamma rays per nuclear decay @@ -57,18 +76,15 @@ static void read_gamma_spectrum(const int nucindex, const char filename[50]) int nlines = 0; assert_always(fscanf(filein, "%d", &nlines) == 1); - gamma_spectra[nucindex].nlines = nlines; - - gamma_spectra[nucindex].energy = std::make_unique(nlines); - gamma_spectra[nucindex].probability = std::make_unique(nlines); + gamma_spectra[nucindex].resize(nlines); double E_gamma_avg = 0.; for (int n = 0; n < nlines; n++) { double en_mev = 0.; double prob = 0.; assert_always(fscanf(filein, "%lg %lg", &en_mev, &prob) == 2); - gamma_spectra[nucindex].energy[n] = en_mev * MEV; - gamma_spectra[nucindex].probability[n] = prob; + gamma_spectra[nucindex][n].energy = en_mev * MEV; + gamma_spectra[nucindex][n].probability = prob; E_gamma_avg += en_mev * MEV * prob; } fclose(filein); @@ -81,54 +97,45 @@ static void read_gamma_spectrum(const int nucindex, const char filename[50]) static void set_trivial_gamma_spectrum(const int nucindex) { // printout("Setting trivial gamma spectrum for z %d a %d engamma %g\n", z, a, decay::nucdecayenergygamma(z, a)); const int nlines = 1; - gamma_spectra[nucindex].nlines = nlines; - gamma_spectra[nucindex].energy = std::make_unique(nlines); - gamma_spectra[nucindex].probability = std::make_unique(nlines); - gamma_spectra[nucindex].energy[0] = decay::nucdecayenergygamma(nucindex); - gamma_spectra[nucindex].probability[0] = 1.; + gamma_spectra[nucindex].resize(nlines); + gamma_spectra[nucindex][0].energy = decay::nucdecayenergygamma(nucindex); + gamma_spectra[nucindex][0].probability = 1.; } static void read_decaydata() { // migrate from old filename - if (!std::ifstream("ni56_lines.txt") && std::ifstream("ni_lines.txt")) { + if (!std::filesystem::exists("ni56_lines.txt") && std::filesystem::exists("ni_lines.txt")) { printout("Moving ni_lines.txt to ni56_lines.txt\n"); std::rename("ni_lines.txt", "ni56_lines.txt"); } // migrate from old filename - if (!std::ifstream("co56_lines.txt") && std::ifstream("co_lines.txt")) { + if (!std::filesystem::exists("co56_lines.txt") && std::filesystem::exists("co_lines.txt")) { printout("Moving co_lines.txt to co56_lines.txt\n"); std::rename("co_lines.txt", "co56_lines.txt"); } - gamma_spectra = static_cast(calloc(decay::get_num_nuclides(), sizeof(struct gamma_spec))); + gamma_spectra.resize(decay::get_num_nuclides()); for (int nucindex = 0; nucindex < decay::get_num_nuclides(); nucindex++) { - gamma_spectra[nucindex].nlines = 0; - gamma_spectra[nucindex].energy = nullptr; - gamma_spectra[nucindex].probability = nullptr; + gamma_spectra[nucindex].clear(); const int z = decay::get_nuc_z(nucindex); const int a = decay::get_nuc_a(nucindex); if (z < 1) { continue; } - const char *elname = decay::get_elname(z); - const size_t elnamelen = strlen(elname); // excluding the NULL terminator - assert_always(elnamelen < 7); - char elnamelower[8]; - for (size_t i = 0; i < elnamelen; i++) { - elnamelower[i] = static_cast(tolower(elname[i])); - } - elnamelower[elnamelen] = '\0'; + auto strelname = decay::get_elname(z); + std::transform(strelname.begin(), strelname.end(), strelname.begin(), + [](unsigned char c) { return std::tolower(c); }); // look in the current folder char filename[MAXFILENAMELENGTH]; - snprintf(filename, MAXFILENAMELENGTH, "%s%d_lines.txt", elnamelower, a); + snprintf(filename, MAXFILENAMELENGTH, "%s%d_lines.txt", strelname.c_str(), a); // look in the 'data' subfolder char filename2[MAXFILENAMELENGTH]; - snprintf(filename2, MAXFILENAMELENGTH, "data/%s%d_lines.txt", elnamelower, a); + snprintf(filename2, MAXFILENAMELENGTH, "data/%s%d_lines.txt", strelname.c_str(), a); if (std::ifstream(filename)) { read_gamma_spectrum(nucindex, filename); @@ -159,24 +166,24 @@ static void read_decaydata() { } // construct an energy ordered gamma ray line list. -void init_gamma_linelist() { +static void init_gamma_linelist() { read_decaydata(); // Now do the sorting. - int total_lines = 0; + std::ptrdiff_t total_lines = 0; for (int nucindex = 0; nucindex < decay::get_num_nuclides(); nucindex++) { - total_lines += gamma_spectra[nucindex].nlines; + total_lines += std::ssize(gamma_spectra[nucindex]); } - printout("total gamma-ray lines %d\n", total_lines); + printout("total gamma-ray lines %td\n", total_lines); - allnuc_gamma_line_list = std::vector(); + allnuc_gamma_line_list = std::vector(); allnuc_gamma_line_list.reserve(total_lines); for (int nucindex = 0; nucindex < decay::get_num_nuclides(); nucindex++) { - for (int j = 0; j < gamma_spectra[nucindex].nlines; j++) { + for (std::ptrdiff_t j = 0; j < std::ssize(gamma_spectra[nucindex]); j++) { allnuc_gamma_line_list.push_back( - {.nucindex = nucindex, .nucgammaindex = j, .energy = gamma_spectra[nucindex].energy[j]}); + {.nucindex = nucindex, .nucgammaindex = static_cast(j), .energy = gamma_spectra[nucindex][j].energy}); } } allnuc_gamma_line_list.shrink_to_fit(); @@ -186,18 +193,55 @@ void init_gamma_linelist() { FILE *const line_list = fopen_required("gammalinelist.out", "w"); fprintf(line_list, "#index nucindex Z A nucgammmaindex en_gamma_mev gammaline_probability\n"); - for (int i = 0; i < total_lines; i++) { + for (std::ptrdiff_t i = 0; i < total_lines; i++) { const int nucindex = allnuc_gamma_line_list[i].nucindex; const int index = allnuc_gamma_line_list[i].nucgammaindex; - fprintf(line_list, "%d %d %d %d %d %g %g \n", i, allnuc_gamma_line_list[i].nucindex, + fprintf(line_list, "%d %d %d %d %d %g %g \n", static_cast(i), allnuc_gamma_line_list[i].nucindex, decay::get_nuc_z(allnuc_gamma_line_list[i].nucindex), decay::get_nuc_a(allnuc_gamma_line_list[i].nucindex), - allnuc_gamma_line_list[i].nucgammaindex, gamma_spectra[nucindex].energy[index] / MEV, - gamma_spectra[nucindex].probability[index]); + allnuc_gamma_line_list[i].nucgammaindex, gamma_spectra[nucindex][index].energy / MEV, + gamma_spectra[nucindex][index].probability); } fclose(line_list); } -void normalise_grey(int nts) { +static void init_xcom_photoion_data() { + // read the file + printout("reading XCOM photoionization data...\n"); + // reserve memory + for (int Z = 0; Z < numb_xcom_elements; Z++) { + photoion_data[Z].reserve(100); + } + std::string filepath{"xcom_photoion_data.txt"}; + if (!std::filesystem::exists(filepath)) { + filepath = "data/xcom_photoion_data.txt"; + } + assert_always(std::filesystem::exists(filepath)); + + std::ifstream data_fs(filepath); + std::string line_str; + // now read the file a second time to store the data + while (getline(data_fs, line_str)) { + if (line_str[0] != '#') { + int Z = 0; + double E = 0; + double sigma = 0; + if (3 == std::sscanf(line_str.c_str(), "%d %lg %lg", &Z, &E, &sigma)) { + assert_always(Z > 0); + assert_always(Z <= numb_xcom_elements); + photoion_data[Z - 1].push_back({.energy = E, .sigma_xcom = sigma}); + } + } + } +} + +void init_gamma_data() { + init_gamma_linelist(); + if constexpr (USE_XCOM_GAMMAPHOTOION) { + init_xcom_photoion_data(); + } +} + +void normalise(int nts) { const double dt = globals::timesteps[nts].width; globals::timesteps[nts].gamma_dep_pathint = 0.; for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { @@ -205,133 +249,121 @@ void normalise_grey(int nts) { const double dV = grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::timesteps[nts].mid / globals::tmin, 3); - globals::timesteps[nts].gamma_dep_pathint += globals::rpkt_emiss[mgi] / globals::nprocs; + globals::timesteps[nts].gamma_dep_pathint += globals::dep_estimator_gamma[nonemptymgi] / globals::nprocs; - globals::rpkt_emiss[mgi] = globals::rpkt_emiss[mgi] * ONEOVER4PI / dV / dt / globals::nprocs; + globals::dep_estimator_gamma[nonemptymgi] = + globals::dep_estimator_gamma[nonemptymgi] * ONEOVER4PI / dV / dt / globals::nprocs; - assert_testmodeonly(globals::rpkt_emiss[mgi] >= 0.); - assert_testmodeonly(isfinite(globals::rpkt_emiss[mgi])); + assert_testmodeonly(globals::dep_estimator_gamma[nonemptymgi] >= 0.); + assert_testmodeonly(std::isfinite(globals::dep_estimator_gamma[nonemptymgi])); } } -static void choose_gamma_ray(struct packet *pkt_ptr) { +static auto choose_gamma_ray(const int nucindex) -> double { // Routine to choose which gamma ray line it'll be. - const int nucindex = pkt_ptr->pellet_nucindex; const double E_gamma = decay::nucdecayenergygamma(nucindex); // Average energy per gamma line of a decay const double zrand = rng_uniform(); - int nselected = -1; double runtot = 0.; - for (int n = 0; n < gamma_spectra[nucindex].nlines; n++) { - runtot += gamma_spectra[nucindex].probability[n] * gamma_spectra[nucindex].energy[n] / E_gamma; + for (ptrdiff_t n = 0; n < std::ssize(gamma_spectra[nucindex]); n++) { + runtot += gamma_spectra[nucindex][n].probability * gamma_spectra[nucindex][n].energy / E_gamma; if (zrand <= runtot) { - nselected = n; - break; + return gamma_spectra[nucindex][n].energy / H; } } - if (nselected < 0) { - printout("Failure to choose line (packet type %d pellet_nucindex %d). Abort. zrand %g runtot %g\n", pkt_ptr->type, - pkt_ptr->pellet_nucindex, zrand, runtot); - abort(); - } - - pkt_ptr->nu_cmf = gamma_spectra[nucindex].energy[nselected] / H; - // printout("%s PELLET %g\n", gammaspec->filename, gammaspec->energy[nselected]); + printout("Failure to choose line (pellet_nucindex %d). Abort. zrand %g runtot %g\n", nucindex, zrand, runtot); + assert_always(false); } -void pellet_gamma_decay(struct packet *pkt_ptr) { +void pellet_gamma_decay(Packet &pkt) { // Subroutine to convert a pellet to a gamma ray (or kpkt if no gamma spec loaded) - // pkt_ptr is a pointer to the packet that is decaying. + // pkt is a pointer to the packet that is decaying. // Start by getting the position of the pellet at the point of decay. Pellet // is moving with the matter. // if no gamma spectra is known, then covert straight to kpkts (e.g., Fe52, Mn52) - if (gamma_spectra[pkt_ptr->pellet_nucindex].nlines == 0) { - pkt_ptr->type = TYPE_KPKT; - pkt_ptr->absorptiontype = -6; + if (gamma_spectra[pkt.pellet_nucindex].empty()) { + pkt.type = TYPE_KPKT; + pkt.absorptiontype = -6; return; } // Now let's give the gamma ray a direction. // Assuming isotropic emission in cmf - double dir_cmf[3]; - get_rand_isotropic_unitvec(dir_cmf); + const auto dir_cmf = get_rand_isotropic_unitvec(); // This direction is in the cmf - we want to convert it to the rest // frame - use aberation of angles. We want to convert from cmf to // rest so need -ve velocity. - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, -1. * pkt_ptr->tdecay); + const auto vel_vec = get_velocity(pkt.pos, -1. * pkt.tdecay); // negative time since we want the backwards transformation here - angle_ab(dir_cmf, vel_vec, pkt_ptr->dir); + pkt.dir = angle_ab(dir_cmf, vel_vec); // Now need to assign the frequency of the packet in the co-moving frame. - choose_gamma_ray(pkt_ptr); + pkt.nu_cmf = choose_gamma_ray(pkt.pellet_nucindex); // Finally we want to put in the rest frame energy and frequency. And record // that it's now a gamma ray. - pkt_ptr->prop_time = pkt_ptr->tdecay; - 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.prop_time = pkt.tdecay; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.nu_rf = pkt.nu_cmf / dopplerfactor; + pkt.e_rf = pkt.e_cmf / dopplerfactor; - pkt_ptr->type = TYPE_GAMMA; - pkt_ptr->last_cross = BOUNDARY_NONE; + pkt.type = TYPE_GAMMA; + pkt.last_cross = BOUNDARY_NONE; // initialise polarisation information - pkt_ptr->stokes[0] = 1.0; - pkt_ptr->stokes[1] = pkt_ptr->stokes[2] = 0.0; - std::array dummy_dir = {0., 0., 1.}; + pkt.stokes[0] = 1.; + pkt.stokes[1] = 0.; + pkt.stokes[2] = 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; - dummy_dir[1] = 1.0; - cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); + pkt.pol_dir = cross_prod(pkt.dir, std::array{0., 0., 1.}); + if ((dot(pkt.pol_dir, pkt.pol_dir)) < 1.e-8) { + pkt.pol_dir = cross_prod(pkt.dir, std::array{0., 1., 0.}); } - vec_norm(pkt_ptr->pol_dir, pkt_ptr->pol_dir); + pkt.pol_dir = vec_norm(pkt.pol_dir); // printout("initialise pol state of packet %g, %g, %g, %g, - // %g\n",pkt_ptr->stokes_qu[0],pkt_ptr->stokes_qu[1],pkt_ptr->pol_dir[0],pkt_ptr->pol_dir[1],pkt_ptr->pol_dir[2]); - // printout("pkt direction %g, %g, %g\n",pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); + // %g\n",pkt.stokes_qu[0],pkt.stokes_qu[1],pkt.pol_dir[0],pkt.pol_dir[1],pkt.pol_dir[2]); + // printout("pkt direction %g, %g, %g\n",pkt.dir[0],pkt.dir[1],pkt.dir[2]); } -constexpr auto sigma_compton_partial(const double x, const double f) -> double +constexpr auto sigma_compton_partial(const double x, const double f_max) -> double // Routine to compute the partial cross section for Compton scattering. // xx is the photon energy (in units of electron mass) and f // is the energy loss factor up to which we wish to integrate. { - const double term1 = ((x * x) - (2 * x) - 2) * std::log(f) / x / x; - const double term2 = (((f * f) - 1) / (f * f)) / 2; - const double term3 = ((f - 1) / x) * ((1 / x) + (2 / f) + (1 / (x * f))); + const double term1 = ((x * x) - (2 * x) - 2) * std::log(f_max) / x / x; + const double term2 = (((f_max * f_max) - 1) / (f_max * f_max)) / 2; + const double term3 = ((f_max - 1) / x) * ((1 / x) + (2 / f_max) + (1 / (x * f_max))); return (3 * SIGMA_T * (term1 + term2 + term3) / (8 * x)); } -static auto get_chi_compton_rf(const struct packet *pkt_ptr) -> double { +static auto get_chi_compton_rf(const Packet &pkt) -> 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. - const double xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; + const double xx = H * pkt.nu_cmf / ME / CLIGHT / CLIGHT; // Use this to decide whether the Thompson limit is acceptable. const double sigma_cmf = (xx < THOMSON_LIMIT) ? SIGMA_T : sigma_compton_partial(xx, 1 + (2 * xx)); // Now need to multiply by the electron number density. - const double chi_cmf = sigma_cmf * grid::get_nnetot(grid::get_cell_modelgridindex(pkt_ptr->where)); + const double chi_cmf = sigma_cmf * grid::get_nnetot(grid::get_cell_modelgridindex(pkt.where)); // convert between frames - const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); assert_testmodeonly(std::isfinite(chi_rf)); @@ -377,9 +409,7 @@ static auto choose_f(const double xx, const double zrand) -> double static auto thomson_angle() -> double { // For Thomson scattering we can get the new angle from a random number very easily. - const double zrand = rng_uniform(); - - const double B_coeff = (8. * zrand) - 4.; + const double B_coeff = (8. * rng_uniform()) - 4.; double t_coeff = sqrt((B_coeff * B_coeff) + 4); t_coeff = t_coeff - B_coeff; @@ -390,20 +420,58 @@ static auto thomson_angle() -> double { if (fabs(mu) > 1) { printout("Error in Thomson. Abort.\n"); - abort(); + std::abort(); } return mu; } -static void compton_scatter(struct packet *pkt_ptr) -// Routine to deal with physical Compton scattering event. +[[nodiscard]] static auto scatter_dir(const std::array dir_in, + const double cos_theta) -> std::array +// Routine for scattering a direction through angle theta. { - double f = NAN; + // begin with setting the direction in coordinates where original direction + // is parallel to z-hat. + + const double phi = rng_uniform() * 2 * PI; + + const double sin_theta_sq = 1. - (cos_theta * cos_theta); + const double sin_theta = std::sqrt(sin_theta_sq); + const double zprime = cos_theta; + const double xprime = sin_theta * cos(phi); + const double yprime = sin_theta * sin(phi); + + // Now need to derotate the coordinates back to real x,y,z. + // 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. / vec_len(dir_in); + + const double r11 = dir_in[1] * norm1; + const double r12 = -1 * dir_in[0] * norm1; + const double r13 = 0.; + const double r21 = dir_in[0] * dir_in[2] * norm1 * norm2; + const double r22 = dir_in[1] * dir_in[2] * norm1 * norm2; + const double r23 = -1 * norm2 / norm1; + const double r31 = dir_in[0] * norm2; + const double r32 = dir_in[1] * norm2; + const double r33 = dir_in[2] * norm2; + + std::array dir_out{(r11 * xprime) + (r21 * yprime) + (r31 * zprime), + (r12 * xprime) + (r22 * yprime) + (r32 * zprime), + (r13 * xprime) + (r23 * yprime) + (r33 * zprime)}; + + assert_testmodeonly(std::fabs(vec_len(dir_out) - 1.) < 1e-10); + + return dir_out; +} + +static void compton_scatter(Packet &pkt) +// Routine to deal with physical Compton scattering event. +{ // printout("Compton scattering.\n"); - const double xx = H * pkt_ptr->nu_cmf / ME / CLIGHT / CLIGHT; + const double xx = H * pkt.nu_cmf / ME / CLIGHT / CLIGHT; // It is known that a Compton scattering event is going to take place. // We need to do two things - (1) decide whether to convert energy @@ -419,49 +487,45 @@ static void compton_scatter(struct packet *pkt_ptr) // sigma_partial/sigma_tot = zrand bool stay_gamma = false; + double f{NAN}; if (xx < THOMSON_LIMIT) { - f = 1.0; // no energy loss + f = 1.; // no energy loss stay_gamma = true; } else { - const double zrand = rng_uniform(); - f = choose_f(xx, zrand); + f = choose_f(xx, rng_uniform()); // Check that f lies between 1.0 and (2xx + 1) if ((f < 1) || (f > (2 * xx + 1))) { printout("Compton f out of bounds. Abort.\n"); - abort(); + std::abort(); } // Prob of keeping gamma ray is... const double prob_gamma = 1. / f; - const double zrand2 = rng_uniform(); - stay_gamma = (zrand2 < prob_gamma); + stay_gamma = (rng_uniform() < prob_gamma); } if (stay_gamma) { // It stays as a gamma ray. Change frequency and direction in // co-moving frame then transfer back to rest frame. - pkt_ptr->nu_cmf = pkt_ptr->nu_cmf / f; // reduce frequency + pkt.nu_cmf = pkt.nu_cmf / f; // reduce frequency // The packet has stored the direction in the rest frame. // Use aberation of angles to get this into the co-moving frame. - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); + auto vel_vec = get_velocity(pkt.pos, pkt.prop_time); - double cmf_dir[3]; - angle_ab(pkt_ptr->dir, vel_vec, cmf_dir); + auto cmf_dir = angle_ab(pkt.dir, vel_vec); // Now change the direction through the scattering angle. const double cos_theta = (xx < THOMSON_LIMIT) ? thomson_angle() : 1. - ((f - 1) / xx); - double new_dir[3] = {NAN, NAN, NAN}; - scatter_dir(cmf_dir, cos_theta, new_dir); + const auto new_dir = scatter_dir(cmf_dir, cos_theta); const double test = dot(new_dir, new_dir); if (fabs(1. - test) > 1.e-8) { @@ -469,88 +533,138 @@ static void compton_scatter(struct packet *pkt_ptr) printout("new_dir %g %g %g\n", new_dir[0], new_dir[1], new_dir[2]); printout("cmf_dir %g %g %g\n", cmf_dir[0], cmf_dir[1], cmf_dir[2]); printout("cos_theta %g", cos_theta); - abort(); + std::abort(); } const double test2 = dot(new_dir, cmf_dir); if (fabs(test2 - cos_theta) > 1.e-8) { printout("Problem with angle - Compton. Abort.\n"); - abort(); + std::abort(); } // Now convert back again. - // get_velocity(pkt_ptr->pos, vel_vec, (-1 * pkt_ptr->prop_time)); - vec_scale(vel_vec, -1.); - - double final_dir[3]; - angle_ab(new_dir, vel_vec, final_dir); + pkt.dir = angle_ab(new_dir, vec_scale(vel_vec, -1.)); - vec_copy(pkt_ptr->dir, final_dir); - - assert_testmodeonly(std::fabs(vec_len(pkt_ptr->dir) - 1.) < 1e-10); + assert_testmodeonly(std::fabs(vec_len(pkt.dir) - 1.) < 1e-10); // 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->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; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.nu_rf = pkt.nu_cmf / dopplerfactor; + pkt.e_rf = pkt.e_cmf / dopplerfactor; - pkt_ptr->last_cross = BOUNDARY_NONE; // allow it to re-cross a boundary + pkt.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; - pkt_ptr->absorptiontype = -3; + pkt.type = TYPE_NTLEPTON; + pkt.absorptiontype = -3; stats::increment(stats::COUNTER_NT_STAT_FROM_GAMMA); } } -static auto get_chi_photo_electric_rf(const struct packet *pkt_ptr) -> double { +static auto get_chi_photo_electric_rf(const Packet &pkt) -> double { // calculate the absorption coefficient [cm^-1] for photo electric effect scattering in the observer reference frame - double chi_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 int mgi = grid::get_cell_modelgridindex(pkt.where); + + if (mgi >= grid::get_npts_model()) { + // empty cell + return 0.; + } + const double rho = grid::get_rho(mgi); if (globals::gamma_kappagrey < 0) { - // Cross sections from Equation 2 of Ambwani & Sutherland (1988), attributed to Veigele (1973) + chi_cmf = 0.; + if (!USE_XCOM_GAMMAPHOTOION) { + // Cross sections from Equation 2 of Ambwani & Sutherland (1988), attributed to Veigele (1973) - // 2.41326e19 Hz = 100 keV / H - const double hnu_over_100kev = pkt_ptr->nu_cmf / 2.41326e+19; + // 2.41326e19 Hz = 100 keV / H + const double hnu_over_100kev = pkt.nu_cmf / 2.41326e+19; - // double sigma_cmf_cno = 0.0448e-24 * pow(hnu_over_100kev, -3.2); + // double sigma_cmf_cno = 0.0448e-24 * pow(hnu_over_100kev, -3.2); - const double sigma_cmf_si = 1.16e-24 * pow(hnu_over_100kev, -3.13); + const double sigma_cmf_si = 1.16e-24 * pow(hnu_over_100kev, -3.13); - const double sigma_cmf_fe = 25.7e-24 * pow(hnu_over_100kev, -3.0); + const double sigma_cmf_fe = 25.7e-24 * pow(hnu_over_100kev, -3.0); - // Now need to multiply by the particle number density. + // Now need to multiply by the particle number density. - const double chi_cmf_si = sigma_cmf_si * (rho / MH / 28); - // Assumes Z = 14. So mass = 28. + const double chi_cmf_si = sigma_cmf_si * (rho / MH / 28); + // Assumes Z = 14. So mass = 28. - const double chi_cmf_fe = sigma_cmf_fe * (rho / MH / 56); - // Assumes Z = 28. So mass = 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); + const double f_fe = grid::get_ffegrp(mgi); - chi_cmf = (chi_cmf_fe * f_fe) + (chi_cmf_si * (1. - f_fe)); + chi_cmf = (chi_cmf_fe * f_fe) + (chi_cmf_si * (1. - f_fe)); + } else { + const double hnu_over_1MeV = pkt.nu_cmf / 2.41326e+20; + const double log10_hnu_over_1MeV = log10(hnu_over_1MeV); + for (int i = 0; i < get_nelements(); i++) { + // determine charge number: + const int Z = get_atomicnumber(i); + auto numb_energies = std::ssize(photoion_data[Z - 1]); + if (numb_energies == 0) { + continue; + } + const double n_i = grid::get_elem_numberdens(mgi, i); // number density in the current cell + if (n_i == 0) { + continue; + } + // get indices of lower and upper boundary + int E_gtr_idx = -1; + + for (int j = 0; j < numb_energies; j++) { + if (photoion_data[Z - 1][j].energy > hnu_over_1MeV) { + E_gtr_idx = j; + break; + } + } + if (E_gtr_idx == 0) { // packet energy smaller than all tabulated values + chi_cmf += photoion_data[Z - 1][0].sigma_xcom * n_i; + continue; + } + if (E_gtr_idx == -1) { // packet energy greater than all tabulated values + chi_cmf += photoion_data[Z - 1][numb_energies - 1].sigma_xcom * n_i; + continue; + } + assert_always(E_gtr_idx > 0); + assert_always(E_gtr_idx < numb_energies); + const int E_smaller_idx = E_gtr_idx - 1; + assert_always(E_smaller_idx >= 0); + const double log10_E = log10_hnu_over_1MeV; + const double log10_E_gtr = log10(photoion_data[Z - 1][E_gtr_idx].energy); + const double log10_E_smaller = log10(photoion_data[Z - 1][E_smaller_idx].energy); + const double log10_sigma_lower = log10(photoion_data[Z - 1][E_smaller_idx].sigma_xcom); + const double log10_sigma_gtr = log10(photoion_data[Z - 1][E_gtr_idx].sigma_xcom); + // interpolate or extrapolate, both linear in log10-log10 space + const double log10_intpol = log10_E_smaller + (log10_sigma_gtr - log10_sigma_lower) / + (log10_E_gtr - log10_E_smaller) * (log10_E - log10_E_smaller); + const double sigma_intpol = pow(10., log10_intpol) * 1.0e-24; // now in cm^2 + const double chi_cmf_contrib = sigma_intpol * n_i; + chi_cmf += chi_cmf_contrib; + } + } } else { chi_cmf = globals::gamma_kappagrey * rho; } // Now convert between frames. - const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + const double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); return chi_rf; } -static auto sigma_pair_prod_rf(const struct packet *pkt_ptr) -> double { +static auto sigma_pair_prod_rf(const Packet &pkt) -> double { // calculate the absorption coefficient [cm^-1] for pair production in the observer reference frame - const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); + const int mgi = grid::get_cell_modelgridindex(pkt.where); const double rho = grid::get_rho(mgi); if (globals::gamma_kappagrey >= 0.) { @@ -558,20 +672,20 @@ static auto sigma_pair_prod_rf(const struct packet *pkt_ptr) -> double { } // 2.46636e+20 Hz = 1022 keV / H - if (pkt_ptr->nu_cmf <= 2.46636e+20) { + if (pkt.nu_cmf <= 2.46636e+20) { return 0.; } // double sigma_cmf_cno; - double sigma_cmf_si = NAN; - double sigma_cmf_fe = NAN; + double sigma_cmf_si{NAN}; + double sigma_cmf_fe{NAN}; const double f_fe = grid::get_ffegrp(mgi); // Cross sections from Equation 2 of Ambwani & Sutherland (1988), attributed to Hubbell (1969) // 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) { + const double hnu_over_mev = pkt.nu_cmf / 2.41326e+20; + if (pkt.nu_cmf > 3.61990e+20) { // sigma_cmf_cno = (0.0481 + (0.301 * (hnu_over_mev - 1.5))) * 49.e-27; sigma_cmf_si = (0.0481 + (0.301 * (hnu_over_mev - 1.5))) * 196.e-27; @@ -600,11 +714,11 @@ static auto sigma_pair_prod_rf(const struct packet *pkt_ptr) -> double { // Now need to convert between frames. - double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + double chi_rf = chi_cmf * doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); if (chi_rf < 0) { printout("Negative pair production sigma. Setting to zero. Abort? %g\n", chi_rf); - chi_rf = 0.0; + chi_rf = 0.; } return chi_rf; @@ -627,31 +741,25 @@ constexpr auto meanf_sigma(const double x) -> double return tot; } -static void rlc_emiss_gamma(const struct packet *pkt_ptr, const double dist) { +static void update_gamma_dep(const Packet &pkt, const double dist, const int mgi, const int nonemptymgi) { // Subroutine to record the heating rate in a cell due to gamma rays. // By heating rate I mean, for now, really the rate at which the code is making // k-packets in that cell which will then convert into r-packets. This is (going // to be) used for the new light_curve syn-style calculation. - // The intention is that rpkt_emiss will contain the emissivity of r-packets + // The intention is that dep_estimator_gamma will contain the emissivity of r-packets // in the co-moving frame (which is going to be isotropic). - // This is only done to order v/c for now. - - // Called with a packet that is about to travel a - // distance dist in the lab frame. - if (!(dist > 0)) { return; } - const double doppler_sq = doppler_squared_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); + const double doppler_sq = doppler_squared_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); - 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; + const double xx = H * pkt.nu_cmf / ME / CLIGHT / CLIGHT; + double heating_cont = ((meanf_sigma(xx) * grid::get_nnetot(mgi)) + get_chi_photo_electric_rf(pkt) + + (sigma_pair_prod_rf(pkt) * (1. - (2.46636e+20 / pkt.nu_cmf)))); + heating_cont = heating_cont * pkt.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. @@ -663,11 +771,11 @@ static void rlc_emiss_gamma(const struct packet *pkt_ptr, const double dist) { // 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); + assert_testmodeonly(std::isfinite(heating_cont)); + atomicadd(globals::dep_estimator_gamma[nonemptymgi], heating_cont); } -void pair_prod(struct packet *pkt_ptr) { +void pair_prod(Packet &pkt) { // Routine to deal with pair production. // In pair production, the original gamma makes an electron positron pair - kinetic energy equal to @@ -676,75 +784,69 @@ void pair_prod(struct packet *pkt_ptr) { // at 0.511 MeV in the local cmf (isotropic). So all the thermal energy goes to the thermal pool // immediately and the remainder goes into gamma-rays at 0.511 MeV. - const double prob_gamma = 1.022 * MEV / (H * pkt_ptr->nu_cmf); + const double prob_gamma = 1.022 * MEV / (H * pkt.nu_cmf); if (prob_gamma < 0) { printout("prob_gamma < 0. pair_prod. Abort. %g\n", prob_gamma); - abort(); + std::abort(); } - const double zrand = rng_uniform(); - - if (zrand > prob_gamma) { + if (rng_uniform() > prob_gamma) { // Convert it to an e-minus packet - actually it could be positron EK too, but this works // for consistency with compton_scatter. - pkt_ptr->type = TYPE_NTLEPTON; - pkt_ptr->absorptiontype = -5; + pkt.type = TYPE_NTLEPTON; + pkt.absorptiontype = -5; stats::increment(stats::COUNTER_NT_STAT_FROM_GAMMA); } else { // The energy goes into emission at 511 keV. - pkt_ptr->nu_cmf = 0.511 * MEV / H; + pkt.nu_cmf = 0.511 * MEV / H; // Now let's give the gamma ray a direction. - double dir_cmf[3]; - get_rand_isotropic_unitvec(dir_cmf); + const auto dir_cmf = get_rand_isotropic_unitvec(); // This direction is in the cmf - we want to convert it to the rest // frame - use aberation of angles. We want to convert from cmf to // rest so need -ve velocity. - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, -1. * pkt_ptr->prop_time); + const auto vel_vec = get_velocity(pkt.pos, -1. * pkt.prop_time); // negative time since we want the backwards transformation here - angle_ab(dir_cmf, vel_vec, pkt_ptr->dir); + pkt.dir = angle_ab(dir_cmf, vel_vec); - 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; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.nu_rf = pkt.nu_cmf / dopplerfactor; + pkt.e_rf = pkt.e_cmf / dopplerfactor; - pkt_ptr->type = TYPE_GAMMA; - pkt_ptr->last_cross = BOUNDARY_NONE; + pkt.type = TYPE_GAMMA; + pkt.last_cross = BOUNDARY_NONE; } } -void do_gamma(struct packet *pkt_ptr, double t2) +void do_gamma(Packet &pkt, double t2) // Now routine for moving a gamma packet. Idea is that we have as input // a gamma packet with known properties at time t1 and we want to follow it // until time t2. { // Assign optical depth to next physical event. And start counter of // optical depth for this path. - double zrand = rng_uniform_pos(); + const double zrand = rng_uniform_pos(); const double tau_next = -1. * log(zrand); - const double tau_current = 0.0; + const double tau_current = 0.; // Start by finding the distance to the crossing of the grid cell // boundaries. sdist is the boundary distance and snext is the // grid cell into which we pass. - int snext = 0; - double sdist = grid::boundary_distance(pkt_ptr->dir, pkt_ptr->pos, pkt_ptr->prop_time, pkt_ptr->where, &snext, - &pkt_ptr->last_cross); + auto [sdist, snext] = grid::boundary_distance(pkt.dir, pkt.pos, pkt.prop_time, pkt.where, &pkt.last_cross); 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; + ? globals::rmax * pkt.prop_time / globals::tmin + : 2 * globals::rmax * (pkt.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, + printout("Unreasonably large sdist (gamma). Abort. %g %g %g\n", globals::rmax, pkt.prop_time / globals::tmin, sdist); - abort(); + std::abort(); } if (sdist < 0) { @@ -754,26 +856,26 @@ void do_gamma(struct packet *pkt_ptr, double t2) if (((snext < 0) && (snext != -99)) || (snext >= grid::ngrid)) { printout("Heading for inappropriate grid cell. Abort.\n"); - printout("Current cell %d, target cell %d.\n", pkt_ptr->where, snext); - abort(); + printout("Current cell %d, target cell %d.\n", pkt.where, snext); + std::abort(); } if (sdist > globals::max_path_step) { sdist = globals::max_path_step; - snext = pkt_ptr->where; + snext = pkt.where; } // Now consider the scattering/destruction processes. // Compton scattering - need to determine the scattering co-efficient. // Routine returns the value in the rest frame. - double chi_compton = 0.0; + double chi_compton = 0.; if (globals::gamma_kappagrey < 0) { - chi_compton = get_chi_compton_rf(pkt_ptr); + chi_compton = get_chi_compton_rf(pkt); } - 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_photo_electric = get_chi_photo_electric_rf(pkt); + const double chi_pair_prod = sigma_pair_prod_rf(pkt); const double chi_tot = chi_compton + chi_photo_electric + chi_pair_prod; assert_testmodeonly(std::isfinite(chi_compton)); @@ -782,87 +884,88 @@ void do_gamma(struct packet *pkt_ptr, double t2) // So distance before physical event is... - const double edist = (tau_next - tau_current) / chi_tot; + const double edist = chi_tot > 0. ? (tau_next - tau_current) / chi_tot : std::numeric_limits::max(); if (edist < 0) { printout("Negative distance (edist). Abort. \n"); - abort(); + std::abort(); } // Find how far it can travel during the time inverval. - const double tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; + const double tdist = (t2 - pkt.prop_time) * CLIGHT_PROP; if (tdist < 0) { printout("Negative distance (tdist). Abort. \n"); - abort(); + std::abort(); } // printout("sdist, tdist, edist %g %g %g\n",sdist, tdist, edist); if ((sdist < tdist) && (sdist < edist)) { - pkt_ptr->prop_time += sdist / 2. / CLIGHT_PROP; - move_pkt(pkt_ptr, sdist / 2.); + move_pkt_withtime(pkt, sdist / 2.); // Move it into the new cell. - if (chi_tot > 0) { - rlc_emiss_gamma(pkt_ptr, sdist); + const int mgi = grid::get_cell_modelgridindex(pkt.where); + const int nonemptymgi = (mgi < grid::get_npts_model()) ? grid::get_modelcell_nonemptymgi(mgi) : -1; + if (chi_tot > 0 && nonemptymgi >= 0) { + update_gamma_dep(pkt, sdist, mgi, nonemptymgi); } - pkt_ptr->prop_time += sdist / 2. / CLIGHT_PROP; - move_pkt(pkt_ptr, sdist / 2.); + move_pkt_withtime(pkt, sdist / 2.); - if (snext != pkt_ptr->where) { - grid::change_cell(pkt_ptr, snext); + if (snext != pkt.where) { + grid::change_cell(pkt, 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.); + move_pkt_withtime(pkt, tdist / 2.); + const int mgi = grid::get_cell_modelgridindex(pkt.where); + const int nonemptymgi = (mgi < grid::get_npts_model()) ? grid::get_modelcell_nonemptymgi(mgi) : -1; - if (chi_tot > 0) { - rlc_emiss_gamma(pkt_ptr, tdist); + if (chi_tot > 0 && nonemptymgi >= 0) { + update_gamma_dep(pkt, tdist, mgi, nonemptymgi); } - pkt_ptr->prop_time = t2; - move_pkt(pkt_ptr, tdist / 2.); + move_pkt_withtime(pkt, tdist / 2.); + pkt.prop_time = t2; // prevent roundoff error } else if ((edist < sdist) && (edist < tdist)) { - pkt_ptr->prop_time += edist / 2. / CLIGHT_PROP; - move_pkt(pkt_ptr, edist / 2.); - if (chi_tot > 0) { - rlc_emiss_gamma(pkt_ptr, edist); + move_pkt_withtime(pkt, edist / 2.); + const int mgi = grid::get_cell_modelgridindex(pkt.where); + const int nonemptymgi = (mgi < grid::get_npts_model()) ? grid::get_modelcell_nonemptymgi(mgi) : -1; + if (chi_tot > 0 && nonemptymgi >= 0) { + update_gamma_dep(pkt, edist, mgi, nonemptymgi); } - pkt_ptr->prop_time += edist / 2. / CLIGHT_PROP; - move_pkt(pkt_ptr, edist / 2.); + move_pkt_withtime(pkt, edist / 2.); // event occurs. Choose which event and call the appropriate subroutine. - zrand = rng_uniform(); - if (chi_compton > (zrand * chi_tot)) { + const double chi_rnd = rng_uniform() * chi_tot; + if (chi_compton > chi_rnd) { // Compton scattering. - compton_scatter(pkt_ptr); - } else if ((chi_compton + chi_photo_electric) > (zrand * chi_tot)) { + compton_scatter(pkt); + } else if ((chi_compton + chi_photo_electric) > chi_rnd) { // 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; + pkt.type = TYPE_NTLEPTON; + pkt.absorptiontype = -4; stats::increment(stats::COUNTER_NT_STAT_FROM_GAMMA); - } else if ((chi_compton + chi_photo_electric + chi_pair_prod) > (zrand * chi_tot)) { + } else if ((chi_compton + chi_photo_electric + chi_pair_prod) > chi_rnd) { // It's a pair production - pair_prod(pkt_ptr); + pair_prod(pkt); } else { - 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 " - "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]); + "Failed to identify event. Gamma (1). chi_compton %g chi_photo_electric %g chi_tot %g chi_rnd %g Abort.\n", + chi_compton, chi_photo_electric, chi_tot, chi_rnd); + const int cellindex = pkt.where; + printout( + " globals::cell[pkt.where].rho %g pkt.nu_cmf %g pkt.dir[0] %g pkt.dir[1] %g " + "pkt.dir[2] %g pkt.pos[0] %g pkt.pos[1] %g pkt.pos[2] %g \n", + grid::get_rho(grid::get_cell_modelgridindex(cellindex)), pkt.nu_cmf, pkt.dir[0], pkt.dir[0], pkt.dir[1], + pkt.dir[2], pkt.pos[1], pkt.pos[2]); - abort(); + std::abort(); } } else { printout("Failed to identify event. Gamma (2). edist %g, sdist %g, tdist %g Abort.\n", edist, sdist, tdist); - abort(); + std::abort(); } } diff --git a/gammapkt.h b/gammapkt.h index 23cd4c830..d38c4497f 100644 --- a/gammapkt.h +++ b/gammapkt.h @@ -1,13 +1,14 @@ +#pragma once #ifndef GAMMAPKT_H #define GAMMAPKT_H #include "packet.h" namespace gammapkt { -void init_gamma_linelist(); -void pellet_gamma_decay(struct packet *pkt_ptr); -void do_gamma(struct packet *pkt_ptr, double t2); -void normalise_grey(int nts); +void init_gamma_data(); +void pellet_gamma_decay(Packet &pkt); +void do_gamma(Packet &pkt, double t2); +void normalise(int nts); } // namespace gammapkt diff --git a/globals.cc b/globals.cc deleted file mode 100644 index 82664375a..000000000 --- a/globals.cc +++ /dev/null @@ -1,162 +0,0 @@ -#include "globals.h" - -#ifdef MPI_ON -#include -#endif - -namespace globals { - -double syn_dir[3]; // vector pointing from origin to observer - -std::unique_ptr timesteps = nullptr; - -double *rpkt_emiss = nullptr; /// Volume estimator for the rpkt emissivity - -int bfestimcount = 0; - -// for USE_LUT_PHOTOION = true -double *corrphotoionrenorm = nullptr; -double *gammaestimator = nullptr; - -// for USE_LUT_BFHEATING = true -double *bfheatingestimator = nullptr; - -double *ffheatingestimator = nullptr; -double *colheatingestimator = nullptr; -#ifdef DO_TITER -double *gammaestimator_save = nullptr; -double *bfheatingestimator_save = nullptr; -double *ffheatingestimator_save = nullptr; -double *colheatingestimator_save = nullptr; -#endif - -int *ecounter = nullptr; -int *acounter = nullptr; - -int nprocs_exspec = 1; -bool do_emission_res = true; - -std::unique_ptr startofline; - -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; - -int opacity_case; // 0 normally, 1 for Fe-grp dependence. - /// MK: 2 for Fe-grp dependence and proportional to 1/rho - /// MK: 3 combination of 1 & 2 depending on a rho_crit - /// MK: 4 non-grey treatment - -/// ATOMIC DATA - -int nlines = -1; -std::vector elements; -const struct linelist_entry *linelist = nullptr; -struct bflist_t *bflist = nullptr; - -// for USE_LUT_BFHEATING = true -double *bfheating_coeff = nullptr; - -struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont = nullptr; - -/// Coolinglist -int ncoolingterms; - -/// PHIXSLIST - -double *allcont_nu_edge = nullptr; -const struct fullphixslist *allcont = nullptr; - -// for either USE_LUT_PHOTOION = true or !USE_LUT_BFHEATING = false -struct groundphixslist *groundcont = nullptr; - -struct phixslist *phixslist = nullptr; -int nbfcontinua = -1; -int nbfcontinua_ground = -1; /// number of bf-continua -int NPHIXSPOINTS = -1; -double NPHIXSNUINCREMENT = -1; - -struct cellhistory *cellhistory = nullptr; - -#ifdef MPI_ON -MPI_Comm mpi_comm_node = MPI_COMM_NULL; -MPI_Comm mpi_comm_internode = MPI_COMM_NULL; -#endif - -int nprocs = -1; // number of MPI processes -int rank_global = -1; // rank of the active MPI process - -int node_nprocs = -1; // number of MPI processes on this node -int rank_in_node = -1; // local rank within this node - -int node_count = -1; // number of MPI nodes -int node_id = -1; // unique number for each node - -constexpr int npkts = MPKTS; -std::atomic nesc = 0; // number of packets that escape during current timestep - -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 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 -double rho_crit_para; /// MK: free parameter for the selection of the critical opacity in opacity_case 3 -double rho_crit; /// MK: critical opacity in opacity_case 3 (could now be declared locally) - -int total_nlte_levels; /// total number of nlte levels - -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 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 0352f5b76..9d2c112e5 100644 --- a/globals.h +++ b/globals.h @@ -1,46 +1,52 @@ +#pragma once #ifndef GLOBALS_H #define GLOBALS_H -#include -#include -#include - #ifdef MPI_ON #include #endif +#include +#include +#include +#include +#include +#include +#include +#include + #include "artisoptions.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] - std::atomic pellet_decays; // Number of pellets that decay in this time step. +struct TimeStep { + 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. }; -struct bflist_t { +struct BFListEntry { int elementindex; int ionindex; int levelindex; int phixstargetindex; }; -struct fullphixslist { +struct FullPhotoionTransition { double nu_edge; int element; int ion; @@ -53,26 +59,18 @@ struct fullphixslist { int bfestimindex; }; -struct groundphixslist { +struct GroundPhotoion { double nu_edge; int element; int ion; - int level; - int phixstargetindex; -}; - -struct phixslist { - double *groundcont_gamma_contr = nullptr; // for either USE_LUT_PHOTOION = true or !USE_LUT_BFHEATING = false - double *chi_bf_sum = nullptr; - double *gamma_contr = nullptr; // needed for DETAILED_BF_ESTIMATORS_ON }; -struct phixstarget_entry { +struct PhotoionTarget { double probability; // fraction of phixs cross section leading to this final level int levelindex; // index of upper ion level after photoionisation }; -struct level_transition { +struct LevelTransition { int lineindex; int targetlevelindex; float einstein_A; @@ -81,58 +79,56 @@ struct level_transition { bool forbidden; }; -struct levellist_entry { - double epsilon; /// Excitation energy of this level relative to the neutral ground level. - struct level_transition *uptrans; /// Allowed upward transitions from this level - struct level_transition *downtrans; /// Allowed downward transitions from this level - int nuptrans; - int ndowntrans; - double phixs_threshold; /// Energy of first point in the photion_xs table - struct phixstarget_entry *phixstargets = nullptr; /// pointer to table of target states and probabilities - float *photoion_xs = nullptr; /// Pointer to a lookup-table providing photoionisation cross-sections for this level. - int nphixstargets; /// length of phixstargets array: - float stat_weight; /// Statistical weight of this level. - - int cont_index; /// Index of the continuum associated to this level. Negative number. - int closestgroundlevelcont; - - int uniquelevelindex; - bool metastable; /// +struct EnergyLevel { + double epsilon{-1}; /// Excitation energy of this level relative to the neutral ground level. + LevelTransition *uptrans{}; /// Allowed upward transitions from this level + LevelTransition *downtrans{}; /// Allowed downward transitions from this level + int nuptrans{0}; + int ndowntrans{0}; + PhotoionTarget *phixstargets{}; /// pointer to table of target states and probabilities + float *photoion_xs{}; /// Pointer to a lookup-table providing photoionisation cross-sections for this level. + int nphixstargets{0}; /// length of phixstargets array: + float stat_weight{0}; /// Statistical weight of this level. + + int cont_index{-1}; /// Index of the continuum associated to this level. Negative number. + int closestgroundlevelcont{-1}; + bool metastable{}; /// }; -struct ionlist_entry { - struct levellist_entry *levels; /// Carries information for each level: 0,1,...,nlevels-1 - int ionstage; /// Which ionisation stage: XI=0, XII=1, XIII=2, ... - int nlevels; /// Number of levels for this ionisation stage - int nlevels_nlte; /// number of nlte levels for this ion - int first_nlte; /// index into nlte_pops array of a grid cell - int ionisinglevels; /// Number of levels which have a bf-continuum - int maxrecombininglevel; /// level index of the highest level with a non-zero recombination rate +struct Ion { + EnergyLevel *levels; /// Carries information for each level: 0,1,...,nlevels-1 + int ionstage; /// Which ionisation stage: XI=0, XII=1, XIII=2, ... + int nlevels; /// Number of levels for this ionisation stage + int nlevels_nlte; /// number of nlte levels for this ion + int first_nlte; /// index into nlte_pops array of a grid cell + int ionisinglevels; /// Number of levels which have a bf-continuum + int maxrecombininglevel; /// level index of the highest level with a non-zero recombination rate int nlevels_groundterm; int coolingoffset; int ncoolingterms; - int uniqueionindex; + int uniquelevelindexstart; + int groundcontindex; float *Alpha_sp; double ionpot; /// Ionisation threshold to the next ionstage // int nbfcontinua; // ionsphixslist *phixslist; }; -struct elementlist_entry { - ionlist_entry *ions; /// Carries information for each ion: 0,1,...,nions-1 - int nions; /// Number of ions for the current element - int anumber; /// Atomic number +struct Element { + Ion *ions{}; /// Carries information for each ion: 0,1,...,nions-1 + int nions{0}; /// Number of ions for the current element + int anumber{-1}; /// Atomic number // int uppermost_ion; /// Highest ionisation stage which has a decent population for a given // cell /// Be aware that this must not be used outside of the update_grid routine /// and their daughters. Neither it will work with OpenMP threads. - int uniqueionindexstart{-1}; /// Index of the lowest ionisation stage of this element - float abundance; /// - float initstablemeannucmass; /// Atomic mass number in multiple of MH - bool has_nlte_levels; + int uniqueionindexstart{-1}; /// Index of the lowest ionisation stage of this element + float abundance{0.}; /// + float initstablemeannucmass = {0.}; /// Atomic mass number in multiple of MH + bool has_nlte_levels{false}; }; -struct linelist_entry { +struct TransitionLine { double nu; /// Frequency of the line transition float einstein_A; int elementindex; /// It's a transition of element (not its atomic number, @@ -142,24 +138,12 @@ struct linelist_entry { int lowerlevelindex; /// and lower levels }; -struct gslintegration_paras { +struct GSLIntegrationParas { double nu_edge; float T; float *photoion_xs; }; -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 = -1; - bool recalculate_required = true; // e.g. when cell or timestep has changed -}; - template struct chphixstargets { double corrphotoioncoeff; @@ -171,150 +155,203 @@ struct chphixstargets { double separatestimrecomb; }; -using chphixstargets_t = struct chphixstargets; - -#include "macroatom.h" +using CellCachePhixsTargets = chphixstargets; + +enum ma_action { + /// Radiative deexcitation rate from this level. + MA_ACTION_RADDEEXC = 0, + /// Collisional deexcitation rate from this level. + MA_ACTION_COLDEEXC = 1, + /// Radiative recombination from this level. + MA_ACTION_RADRECOMB = 2, + /// Collisional recombination rate from this level. + MA_ACTION_COLRECOMB = 3, + /// Rate for internal downward transitions to same ionisation stage. + MA_ACTION_INTERNALDOWNSAME = 4, + /// Rate for internal upward transitions to same ionisation stage. + MA_ACTION_INTERNALDOWNLOWER = 5, + /// Rate for internal downward transitions to lower ionisation stage. + MA_ACTION_INTERNALUPSAME = 6, + /// Rate for internal upward transitions to higher ionisation stage. + MA_ACTION_INTERNALUPHIGHER = 7, + /// Rate for internal upward transitions to higher ionisation stage due to non-thermal collisions. + MA_ACTION_INTERNALUPHIGHERNT = 8, + MA_ACTION_COUNT = 9, +}; -struct chlevels { +struct CellCacheLevels { std::array processrates; - chphixstargets_t *chphixstargets; - double bfheatingcoeff; + CellCachePhixsTargets *chphixstargets; double population; double *sum_epstrans_rad_deexc; double *sum_internal_down_same; double *sum_internal_up_same; }; -struct chions { - struct chlevels *chlevels; /// Pointer to the ions levellist. +struct CellCacheIons { + CellCacheLevels *chlevels; /// Pointer to the ions levellist. }; -struct chelements { - struct chions *chions; /// Pointer to the elements ionlist. +struct CellCacheElements { + CellCacheIons *chions; /// Pointer to the elements ionlist. }; -struct cellhistory { - double *cooling_contrib; /// Cooling contributions by the different processes. - struct chelements *chelements; - struct chlevels *ch_all_levels; - double *ch_allcont_departureratios; - int cellnumber; /// Identifies the cell the data is valid for. - int bfheating_mgi; +struct CellCache { + double *cooling_contrib{}; /// Cooling contributions by the different processes. + CellCacheElements *chelements{}; + CellCacheLevels *ch_all_levels{}; + double *ch_allcont_departureratios{}; + double chi_ff_nnionpart{-1}; + int cellnumber{-1}; /// Identifies the cell the data is valid for. }; namespace globals { -extern double syn_dir[3]; // vector pointing from origin to observer +inline std::array syn_dir{}; // vector pointing from origin to observer -extern std::unique_ptr timesteps; +inline std::vector timesteps; -extern double *rpkt_emiss; +inline std::vector dep_estimator_gamma; -extern int bfestimcount; +inline int bfestimcount{0}; // for USE_LUT_PHOTOION = true -extern double *corrphotoionrenorm; -extern double *gammaestimator; +inline double *corrphotoionrenorm{}; +inline double *gammaestimator{}; // for USE_LUT_BFHEATING = true -extern double *bfheatingestimator; +inline double *bfheatingestimator{}; -extern double *ffheatingestimator; -extern double *colheatingestimator; +inline double *ffheatingestimator{}; +inline double *colheatingestimator{}; #ifdef DO_TITER -extern double *gammaestimator_save; -extern double *bfheatingestimator_save; -extern double *ffheatingestimator_save; -extern double *colheatingestimator_save; +inline double *gammaestimator_save{}; +inline double *bfheatingestimator_save{}; +inline double *ffheatingestimator_save{}; +inline double *colheatingestimator_save{}; #endif -extern int *ecounter; -extern int *acounter; - -extern int nprocs_exspec; -extern bool do_emission_res; +inline int *ecounter{}; +inline int *acounter{}; -extern std::unique_ptr startofline; +inline int nprocs_exspec{1}; +inline bool do_emission_res{true}; -extern double gamma_kappagrey; +inline double gamma_kappagrey{}; // set to -ve for proper treatment. If positive, then + // gamma_rays are treated as grey with this opacity. constexpr double GREY_OP = 0.1; -extern double max_path_step; +inline double max_path_step; -extern int opacity_case; +inline int opacity_case{}; // 0 grey, 1 for Fe-grp dependence. + // MK: 2 for Fe-grp dependence and proportional to 1/rho + // MK: 3 combination of 1 & 2 depending on a rho_crit + // MK: 4 non-grey treatment -extern int nlines; -extern std::vector elements; +/// ATOMIC DATA -extern const struct linelist_entry *linelist; -extern struct bflist_t *bflist; +inline int nlines{-1}; +inline std::vector elements; -// for USE_LUT_BFHEATING = true -extern double *bfheating_coeff; +inline const TransitionLine *linelist{}; +inline std::vector bflist; -extern struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont; +inline double *bfheating_coeff{}; // for USE_LUT_BFHEATING = true -extern int ncoolingterms; - -extern double *allcont_nu_edge; -extern const struct fullphixslist *allcont; +inline std::vector bfestim_nu_edge; +inline std::vector allcont_nu_edge; +inline const FullPhotoionTransition *allcont{}; // for either USE_LUT_PHOTOION = true or !USE_LUT_BFHEATING = false -extern struct groundphixslist *groundcont; +inline GroundPhotoion *groundcont{}; + +inline int nbfcontinua{-1}; // number of bf-continua +inline int nbfcontinua_ground{-1}; // number of bf-continua from ground levels -extern struct phixslist *phixslist; -extern int nbfcontinua; -extern int nbfcontinua_ground; -extern int NPHIXSPOINTS; -extern double NPHIXSNUINCREMENT; +inline int NPHIXSPOINTS{-1}; +inline double NPHIXSNUINCREMENT{-1}; -extern struct cellhistory *cellhistory; +inline CellCache *cellcache{}; #ifdef MPI_ON -extern MPI_Comm mpi_comm_node; -extern MPI_Comm mpi_comm_internode; +inline MPI_Comm mpi_comm_node{MPI_COMM_NULL}; +inline MPI_Comm mpi_comm_internode{MPI_COMM_NULL}; #endif -extern int nprocs; -extern int rank_global; +inline int nprocs{-1}; +inline int rank_global{-1}; + +inline int node_nprocs{-1}; +inline int rank_in_node{-1}; -extern int node_nprocs; -extern int rank_in_node; +inline int node_count{-1}; +inline int node_id{-1}; -extern int node_count; -extern int node_id; +inline constexpr int npkts = MPKTS; +inline int nesc{0}; -extern const int npkts; -extern std::atomic nesc; +inline double vmax; +inline double rmax; +inline double tmax{-1}; +inline double tmin{-1}; -extern double vmax; -extern double rmax; -extern double tmax; -extern double tmin; +inline int ntimesteps{-1}; +inline int timestep_initial{-1}; +inline int timestep_finish{-1}; +inline int timestep{-1}; // Current time step during the simulation -extern int ntimesteps; -extern int timestep_initial; -extern int timestep_finish; -extern int timestep; +inline double opcase3_normal; // MK: normalisation factor for opacity_case 3 +inline double rho_crit_para; // MK: free parameter for the selection of the critical opacity in opacity_case 3 +inline double rho_crit; // MK: critical opacity in opacity_case 3 (could now be declared locally) -extern double opcase3_normal; -extern double rho_crit_para; -extern double rho_crit; +inline int total_nlte_levels; -extern int total_nlte_levels; +inline bool simulation_continued_from_saved; +inline double nu_rfcut; +inline int num_lte_timesteps; +inline double cell_is_optically_thick; +inline int num_grey_timesteps; +inline int n_titer; +inline bool lte_iteration; -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 lte_iteration; -extern int n_kpktdiffusion_timesteps; -extern float kpktdiffusion_timescale; +inline std::deque mutex_cellcachemacroatom; -void setup_mpi_vars(); +inline 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 diff --git a/grid.cc b/grid.cc index 7f5d20627..fd461d13a 100644 --- a/grid.cc +++ b/grid.cc @@ -1,17 +1,26 @@ #include "grid.h" +#ifdef MPI_ON +#include +#endif + #include +#include +#include #include #include +#include #include +#include #include #include #include +#include #include -#include -#include #include #include +#include +#include #include #include "artisoptions.h" @@ -20,33 +29,29 @@ #include "decay.h" #include "globals.h" #include "input.h" -#include "rpkt.h" -#ifdef MPI_ON -#include "mpi.h" -#endif #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 { -struct modelgrid_t *modelgrid = nullptr; +namespace { + +std::array coordlabel{'?', '?', '?'}; -int ncoordgrid[3]; /// propagation grid dimensions -int ngrid; -char coordlabel[3]; +std::array ncoordgrid{0}; /// propagation grid dimensions enum gridtypes model_type = GRID_SPHERICAL1D; size_t npts_model = 0; // number of model grid cells size_t 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 *vout_model{}; +std::array ncoord_model{0}; // the model.txt input grid dimensions double min_den; // minimum model density @@ -55,13 +60,18 @@ double mfeg; /// Total mass of Fe group elements in ejecta int first_cellindex = -1; // auto-dermine first cell index in model.txt (usually 1 or 0) -struct gridcell *cell = nullptr; +struct PropGridCell { + std::array pos_min{}; // Initial co-ordinates of inner most corner of cell. + int modelgridindex{-1}; +}; -static std::vector mg_associated_cells; -static std::vector nonemptymgi_of_mgi; -static std::vector mgi_of_nonemptymgi; +struct PropGridCell *cell{}; -double *totmassradionuclide = nullptr; /// total mass of each radionuclide in the ejecta +std::vector mg_associated_cells; +std::vector nonemptymgi_of_mgi; +std::vector mgi_of_nonemptymgi; + +double *totmassradionuclide{}; /// total mass of each radionuclide in the ejecta #ifdef MPI_ON MPI_Win win_nltepops_allcells = MPI_WIN_NULL; @@ -69,13 +79,17 @@ MPI_Win win_initradioabund_allcells = MPI_WIN_NULL; MPI_Win win_corrphotoionrenorm = MPI_WIN_NULL; #endif -float *initradioabund_allcells = nullptr; +float *initradioabund_allcells{}; +float *initmassfracstable_allcells{}; +float *elem_meanweight_allcells{}; std::vector ranks_nstart; std::vector ranks_ndo; std::vector ranks_ndo_nonempty; int maxndo = -1; +} // anonymous namespace + 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) @@ -181,7 +195,8 @@ static auto get_cell_r_inner(const int cellindex) -> double { } 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) +// how much do we change the cellindex to move along a coordinately axis (e.g., the x, y, z directions, or r +// direction) { // assert_testmodeonly(axis < get_ngriddimensions()); @@ -196,9 +211,12 @@ auto get_coordcellindexincrement(const int axis) -> int return ncoordgrid[0] * ncoordgrid[1]; default: - printout("invalid coordinate index %d", axis); - abort(); - return -1; + if constexpr (TESTMODE) { + printout("invalid coordinate index %d", axis); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); + } } } @@ -219,9 +237,12 @@ auto get_cellcoordpointnum(const int cellindex, const int axis) -> int return (cellindex / (ncoordgrid[0] * ncoordgrid[1])) % ncoordgrid[2]; default: - printout("invalid coordinate index %d", axis); - abort(); - return -1; + if constexpr (TESTMODE) { + printout("invalid coordinate index %d", axis); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); + } } } @@ -232,11 +253,11 @@ auto get_cellcoordpointnum(const int cellindex, const int axis) -> int assert_always(false); } -auto get_rho_tmin(int modelgridindex) -> float { return modelgrid[modelgridindex].rhoinit; } +auto get_rho_tmin(const int modelgridindex) -> float { return modelgrid[modelgridindex].rhoinit; } -auto get_rho(int modelgridindex) -> float { return modelgrid[modelgridindex].rho; } +auto get_rho(const int modelgridindex) -> float { return modelgrid[modelgridindex].rho; } -auto get_nne(int modelgridindex) -> float { +auto get_nne(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex < (get_npts_model() + 1)); @@ -245,7 +266,7 @@ auto get_nne(int modelgridindex) -> float { return nne; } -auto get_nnetot(int modelgridindex) -> float { +auto get_nnetot(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex < (get_npts_model() + 1)); @@ -254,64 +275,72 @@ auto get_nnetot(int modelgridindex) -> float { return nnetot; } -auto get_ffegrp(int modelgridindex) -> float { return modelgrid[modelgridindex].ffegrp; } +auto get_ffegrp(const int modelgridindex) -> float { return modelgrid[modelgridindex].ffegrp; } -void set_elem_abundance(int modelgridindex, int element, float newabundance) +void set_elem_abundance(const int modelgridindex, const int element, const float newabundance) // mass fraction of an element (all isotopes combined) { modelgrid[modelgridindex].composition[element].abundance = newabundance; } -auto get_elem_numberdens(int modelgridindex, int element) -> double +auto get_elem_numberdens(const int modelgridindex, const int element) -> double // mass fraction of an element (all isotopes combined) { const double elem_meanweight = grid::get_element_meanweight(modelgridindex, element); return get_elem_abundance(modelgridindex, element) / elem_meanweight * grid::get_rho(modelgridindex); } -auto get_kappagrey(int modelgridindex) -> float { +auto get_kappagrey(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex <= get_npts_model()); return modelgrid[modelgridindex].kappagrey; } -auto get_Te(int modelgridindex) -> float { +auto get_Te(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex <= get_npts_model()); return modelgrid[modelgridindex].Te; } -auto get_TR(int modelgridindex) -> float { +auto get_TR(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex <= get_npts_model()); return modelgrid[modelgridindex].TR; } -auto get_TJ(int modelgridindex) -> float { +auto get_TJ(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex <= get_npts_model()); return modelgrid[modelgridindex].TJ; } -auto get_W(int modelgridindex) -> float { +auto get_W(const int modelgridindex) -> float { assert_testmodeonly(modelgridindex >= 0); assert_testmodeonly(modelgridindex <= get_npts_model()); return modelgrid[modelgridindex].W; } -static void set_rho_tmin(int modelgridindex, float x) { modelgrid[modelgridindex].rhoinit = x; } +static void set_rho_tmin(const int modelgridindex, const float x) { modelgrid[modelgridindex].rhoinit = x; } -void set_rho(int modelgridindex, float x) { modelgrid[modelgridindex].rho = x; } +void set_rho(const int modelgridindex, float rho) { + assert_always(rho >= 0.); + assert_always(std::isfinite(rho)); + modelgrid[modelgridindex].rho = rho; +} -void set_nne(int modelgridindex, float nne) { modelgrid[modelgridindex].nne = nne; } +void set_nne(const int modelgridindex, float nne) { + assert_always(nne >= 0.); + assert_always(std::isfinite(nne)); + modelgrid[modelgridindex].nne = nne; +} -void set_nnetot(int modelgridindex, float x) { - assert_always(x >= 0.); - assert_always(std::isfinite(x)); - modelgrid[modelgridindex].nnetot = x; +void set_nnetot(const int modelgridindex, float nnetot) { + assert_always(nnetot >= 0.); + assert_always(std::isfinite(nnetot)); + modelgrid[modelgridindex].nnetot = nnetot; } -static void set_ffegrp(int modelgridindex, float x) { +static void set_ffegrp(const int modelgridindex, float x) { if (!(x >= 0.)) { printout("WARNING: Fe-group mass fraction %g is negative in cell %d\n", x, modelgridindex); assert_always(x > -1e-6); @@ -323,15 +352,15 @@ static void set_ffegrp(int modelgridindex, float x) { modelgrid[modelgridindex].ffegrp = x; } -void set_kappagrey(int modelgridindex, float kappagrey) { modelgrid[modelgridindex].kappagrey = kappagrey; } +void set_kappagrey(const int modelgridindex, float kappagrey) { modelgrid[modelgridindex].kappagrey = kappagrey; } -void set_Te(int modelgridindex, float Te) { modelgrid[modelgridindex].Te = Te; } +void set_Te(const int modelgridindex, float Te) { modelgrid[modelgridindex].Te = Te; } -void set_TR(int modelgridindex, float TR) { modelgrid[modelgridindex].TR = TR; } +void set_TR(const int modelgridindex, float TR) { modelgrid[modelgridindex].TR = TR; } -void set_TJ(int modelgridindex, float TJ) { modelgrid[modelgridindex].TJ = TJ; } +void set_TJ(const int modelgridindex, float TJ) { modelgrid[modelgridindex].TJ = TJ; } -void set_W(int modelgridindex, float W) { modelgrid[modelgridindex].W = W; } +void set_W(const int modelgridindex, float W) { modelgrid[modelgridindex].W = W; } auto get_model_type() -> enum gridtypes { return model_type; } @@ -340,14 +369,14 @@ void set_model_type(enum gridtypes model_type_value) { model_type = model_type_v auto get_npts_model() -> int // number of model grid cells { - assert_always(npts_model > 0); + assert_testmodeonly(npts_model > 0); return npts_model; } auto get_nonempty_npts_model() -> int // number of model grid cells { - assert_always(nonempty_npts_model > 0); + assert_testmodeonly(nonempty_npts_model > 0); return nonempty_npts_model; } @@ -355,10 +384,10 @@ static void set_npts_model(int new_npts_model) { npts_model = new_npts_model; assert_always(modelgrid == nullptr); - modelgrid = static_cast(calloc(npts_model + 1, sizeof(struct modelgrid_t))); + modelgrid = static_cast(calloc(npts_model + 1, sizeof(ModelGridCell))); assert_always(modelgrid != nullptr); - mg_associated_cells.resize(npts_model + 1); - nonemptymgi_of_mgi.resize(npts_model + 1); + mg_associated_cells.resize(npts_model + 1, 0); + nonemptymgi_of_mgi.resize(npts_model + 1, -1); } static void allocate_initradiobund() { @@ -384,8 +413,9 @@ static void allocate_initradiobund() { #else initradioabund_allcells = static_cast(malloc(totalradioabundsize)); #endif - printout("[info] mem_usage: radioabundance data for %d nuclides for %d cells occupies %.3f MB (node shared memory)\n", - num_nuclides, npts_model, static_cast(totalradioabundsize) / 1024. / 1024.); + printout( + "[info] mem_usage: radioabundance data for %zu nuclides for %zu cells occupies %.3f MB (node shared memory)\n", + num_nuclides, npts_model, static_cast(totalradioabundsize) / 1024. / 1024.); #ifdef MPI_ON MPI_Barrier(globals::mpi_comm_node); @@ -413,7 +443,7 @@ auto get_t_model() -> double return t_model; } -auto get_cell_modelgridindex(int cellindex) -> int { +auto get_cell_modelgridindex(const int cellindex) -> int { assert_testmodeonly(cellindex >= 0); assert_testmodeonly(cellindex < ngrid); const int mgi = cell[cellindex].modelgridindex; @@ -422,7 +452,7 @@ auto get_cell_modelgridindex(int cellindex) -> int { return mgi; } -static void set_cell_modelgridindex(int cellindex, int new_modelgridindex) { +static void set_cell_modelgridindex(const int cellindex, const int new_modelgridindex) { assert_testmodeonly(cellindex < ngrid); assert_testmodeonly(new_modelgridindex <= get_npts_model()); cell[cellindex].modelgridindex = new_modelgridindex; @@ -435,7 +465,7 @@ auto get_numassociatedcells(const int modelgridindex) -> int return mg_associated_cells[modelgridindex]; } -auto get_modelcell_nonemptymgi(int mgi) -> int +auto get_modelcell_nonemptymgi(const int mgi) -> int // get the index in the list of non-empty cells for a given model grid cell { assert_testmodeonly(get_nonempty_npts_model() > 0); @@ -449,7 +479,7 @@ auto get_modelcell_nonemptymgi(int mgi) -> int return nonemptymgi; } -auto get_mgi_of_nonemptymgi(int nonemptymgi) -> int +auto get_mgi_of_nonemptymgi(const int nonemptymgi) -> int // get the index in the list of non-empty cells for a given model grid cell { assert_testmodeonly(get_nonempty_npts_model() > 0); @@ -476,7 +506,7 @@ static void set_modelinitradioabund(const int modelgridindex, const int nucindex // initradioabund array is in node shared memory assert_always(nucindex >= 0); if (!(abund >= 0.)) { - printout("WARNING: nuclear mass fraction for nucindex %d = %g is negative in cell %d\n", abund, nucindex, + printout("WARNING: nuclear mass fraction for nucindex %d = %g is negative in cell %d\n", nucindex, abund, modelgridindex); assert_always(abund > -1e-6); abund = 0.; @@ -506,9 +536,10 @@ auto get_element_meanweight(const int mgi, const int element) -> float return globals::elements[element].initstablemeannucmass; } -void set_element_meanweight(const int mgi, const int element, float meanweight) +void set_element_meanweight(const int mgi, const int element, const float meanweight) // weight is in grams { + assert_always(meanweight > 0.); modelgrid[mgi].elem_meanweight[element] = meanweight; } @@ -616,7 +647,7 @@ static auto get_cellradialposmid(const int cellindex) -> double } // cubic grid requires taking the length of the 3D position vector - double dcen[3]; + std::array dcen{}; for (int axis = 0; axis < 3; axis++) { dcen[axis] = get_cellcoordmin(cellindex, axis) + (0.5 * wid_init(cellindex, axis)); } @@ -633,9 +664,9 @@ void set_elements_uppermost_ion(const int modelgridindex, const int element, con } static void calculate_kappagrey() { - double rho_sum = 0.0; - double fe_sum = 0.0; - double opcase3_sum = 0.0; + double rho_sum = 0.; + double fe_sum = 0.; + double opcase3_sum = 0.; const int empty_cells = 0; for (int n = 0; n < ngrid; n++) { @@ -656,26 +687,28 @@ static void calculate_kappagrey() { set_kappagrey(mgi, 0.); } else if (get_rho_tmin(mgi) < 0.) { printout("Error: negative density. Abort.\n"); - abort(); + std::abort(); } opcase3_sum += get_kappagrey(mgi) * get_rho_tmin(mgi); } } - FILE *grid_file = nullptr; if (globals::rank_global == 0) { - grid_file = fopen_required("grid.out", "w"); + FILE *grid_file = fopen_required("grid.out", "w"); + for (int n = 0; n < ngrid; n++) { + const int mgi = get_cell_modelgridindex(n); + if (mgi != get_npts_model()) { + fprintf(grid_file, "%d %d\n", n, mgi); /// write only non-empty cells to grid file + } + } + fclose(grid_file); } /// Second pass through allows calculation of normalized chi_grey - double check1 = 0.0; - double check2 = 0.0; - for (int n = 0; n < ngrid; n++) { - const int mgi = get_cell_modelgridindex(n); - if (globals::rank_global == 0 && mgi != get_npts_model()) { - fprintf(grid_file, "%d %d\n", n, mgi); /// write only non-empty cells to grid file - } - + double check1 = 0.; + double check2 = 0.; + for (int nonemptymgi = 0; nonemptymgi < get_nonempty_npts_model(); nonemptymgi++) { + const int mgi = get_mgi_of_nonemptymgi(nonemptymgi); if (get_rho_tmin(mgi) > 0) { double kappa = 0.; if (globals::opacity_case == 0) { @@ -711,7 +744,7 @@ static void calculate_kappagrey() { } } else { printout("Unknown opacity case. Abort.\n"); - abort(); + std::abort(); } set_kappagrey(mgi, kappa); @@ -719,24 +752,28 @@ static void calculate_kappagrey() { set_kappagrey(mgi, 0.); } else if (get_rho_tmin(mgi) < 0.) { printout("Error: negative density. Abort.\n"); - abort(); + std::abort(); } check1 = check1 + (get_kappagrey(mgi) * get_rho_tmin(mgi)); check2 = check2 + get_rho_tmin(mgi); } - if (globals::rank_global == 0) { - fclose(grid_file); - } - printout("Grey normalisation check: %g\n", check1 / check2); } -static void allocate_composition_cooling() +static void allocate_nonemptycells_composition_cooling() /// Initialise composition dependent cell data for the given cell { - const int npts_nonempty = get_nonempty_npts_model(); // add one for the combined empty cell at the end + const size_t npts_nonempty = get_nonempty_npts_model(); + +#ifdef MPI_ON + int my_rank_nonemptycells = nonempty_npts_model / globals::node_nprocs; + // rank_in_node 0 gets any remainder + if (globals::rank_in_node == 0) { + my_rank_nonemptycells += nonempty_npts_model - (my_rank_nonemptycells * globals::node_nprocs); + } +#endif #ifdef MPI_ON size_t my_rank_cells_nonempty = nonempty_npts_model / globals::node_nprocs; @@ -746,9 +783,6 @@ static void allocate_composition_cooling() } #endif - float *initmassfracstable_allcells = nullptr; - float *elem_meanweight_allcells = nullptr; - #ifdef MPI_ON { MPI_Aint size = my_rank_cells_nonempty * get_nelements() * sizeof(float); @@ -760,7 +794,7 @@ static void allocate_composition_cooling() } { - MPI_Aint size = my_rank_cells_nonempty * get_nelements() * sizeof(float); + auto size = static_cast(my_rank_cells_nonempty * get_nelements() * sizeof(float)); int disp_unit = sizeof(float); MPI_Win mpiwin = MPI_WIN_NULL; @@ -774,10 +808,10 @@ static void allocate_composition_cooling() elem_meanweight_allcells = static_cast(malloc(npts_nonempty * get_nelements() * sizeof(float))); #endif - double *nltepops_allcells = nullptr; + double *nltepops_allcells{}; if (globals::total_nlte_levels > 0) { #ifdef MPI_ON - MPI_Aint size = my_rank_cells_nonempty * globals::total_nlte_levels * sizeof(double); + auto size = static_cast(my_rank_nonemptycells * globals::total_nlte_levels * sizeof(double)); int disp_unit = sizeof(double); assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &nltepops_allcells, &win_nltepops_allcells) == MPI_SUCCESS); @@ -791,7 +825,7 @@ static void allocate_composition_cooling() assert_always(nltepops_allcells != nullptr); } - for (int nonemptymgi = 0; nonemptymgi < npts_nonempty; nonemptymgi++) { + for (size_t nonemptymgi = 0; nonemptymgi < npts_nonempty; nonemptymgi++) { const int modelgridindex = grid::get_mgi_of_nonemptymgi(nonemptymgi); modelgrid[modelgridindex].elements_uppermost_ion = static_cast(malloc(get_nelements() * sizeof(int))); @@ -799,11 +833,11 @@ static void allocate_composition_cooling() assert_always(modelgrid[modelgridindex].elements_uppermost_ion != nullptr); modelgrid[modelgridindex].composition = - static_cast(malloc(get_nelements() * sizeof(struct compositionlist_entry))); + static_cast(malloc(get_nelements() * sizeof(ModelCellElement))); if (modelgrid[modelgridindex].composition == nullptr) { printout("[fatal] input: not enough memory to initialize compositionlist for cell %d... abort\n", modelgridindex); - abort(); + std::abort(); } modelgrid[modelgridindex].initmassfracstable = &initmassfracstable_allcells[nonemptymgi * get_nelements()]; @@ -819,8 +853,8 @@ static void allocate_composition_cooling() assert_always(modelgrid[modelgridindex].nlte_pops != nullptr); for (int nlteindex = 0; nlteindex < globals::total_nlte_levels; nlteindex++) { - modelgrid[modelgridindex].nlte_pops[nlteindex] = -1.0; /// flag to indicate that there is - /// currently no information on the nlte populations + modelgrid[modelgridindex].nlte_pops[nlteindex] = -1.; /// flag to indicate that there is + /// currently no information on the nlte populations } } else { modelgrid[modelgridindex].nlte_pops = nullptr; @@ -837,7 +871,7 @@ static void allocate_composition_cooling() printout( "[fatal] input: not enough memory to initialize groundlevelpoplist for element %d in cell %d... abort\n", element, modelgridindex); - abort(); + std::abort(); } modelgrid[modelgridindex].composition[element].partfunct = @@ -846,7 +880,7 @@ static void allocate_composition_cooling() if (modelgrid[modelgridindex].composition[element].partfunct == nullptr) { printout("[fatal] input: not enough memory to initialize partfunctlist for element %d in cell %d... abort\n", element, modelgridindex); - abort(); + std::abort(); } } @@ -854,7 +888,7 @@ static void allocate_composition_cooling() if (modelgrid[modelgridindex].cooling_contrib_ion == nullptr) { printout("[fatal] input: not enough memory to initialize coolinglist for cell %d... abort\n", modelgridindex); - abort(); + std::abort(); } modelgrid[modelgridindex].cooling_contrib_ion[0] = @@ -879,18 +913,6 @@ static void allocate_composition_cooling() } static void allocate_nonemptymodelcells() { - /// This is the placeholder for empty cells. Temperatures must be positive - /// as long as ff opacities are calculated. - set_rho_tmin(get_npts_model(), 0.); - set_rho(get_npts_model(), 0.); - set_nne(get_npts_model(), 0.); - set_nnetot(get_npts_model(), 0.); - set_ffegrp(get_npts_model(), 0.); - - set_Te(get_npts_model(), MINTEMP); - set_TJ(get_npts_model(), MINTEMP); - set_TR(get_npts_model(), MINTEMP); - // 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; @@ -933,7 +955,7 @@ static void allocate_nonemptymodelcells() { if (get_numassociatedcells(mgi) > 0) { if (get_rho_tmin(mgi) <= 0) { printout("Error: negative or zero density. Abort.\n"); - abort(); + std::abort(); } nonemptymgi_of_mgi[mgi] = nonemptymgi; mgi_of_nonemptymgi[nonemptymgi] = mgi; @@ -950,11 +972,15 @@ static void allocate_nonemptymodelcells() { } } - allocate_composition_cooling(); + allocate_nonemptycells_composition_cooling(); - globals::rpkt_emiss = static_cast(calloc((get_npts_model() + 1), sizeof(double))); + if constexpr (EXPANSIONOPACITIES_ON) { + allocate_expansionopacities(); + } + + globals::dep_estimator_gamma.resize(get_nonempty_npts_model(), 0.); - size_t ionestimsize = get_nonempty_npts_model() * get_includedions() * sizeof(double); + auto ionestimsize = get_nonempty_npts_model() * globals::nbfcontinua_ground * sizeof(double); #ifdef MPI_ON if constexpr (USE_LUT_PHOTOION) { @@ -964,7 +990,7 @@ static void allocate_nonemptymodelcells() { my_rank_cells += get_nonempty_npts_model() - (my_rank_cells * globals::node_nprocs); } - auto size = static_cast(my_rank_cells * get_includedions() * sizeof(double)); + auto size = static_cast(my_rank_cells * globals::nbfcontinua_ground * sizeof(double)); int disp_unit = sizeof(double); assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &globals::corrphotoionrenorm, &win_corrphotoionrenorm) == MPI_SUCCESS); @@ -992,11 +1018,11 @@ static void allocate_nonemptymodelcells() { #endif } - globals::ffheatingestimator = static_cast(malloc((get_npts_model() + 1) * sizeof(double))); - globals::colheatingestimator = static_cast(malloc((get_npts_model() + 1) * sizeof(double))); + globals::ffheatingestimator = static_cast(malloc(get_nonempty_npts_model() * sizeof(double))); + globals::colheatingestimator = static_cast(malloc(get_nonempty_npts_model() * sizeof(double))); #ifdef DO_TITER - globals::ffheatingestimator_save = static_cast(malloc((get_npts_model() + 1) * sizeof(double))); - globals::colheatingestimator_save = static_cast(malloc((get_npts_model() + 1) * sizeof(double))); + globals::ffheatingestimator_save = static_cast(malloc(get_nonempty_npts_model() * sizeof(double))); + globals::colheatingestimator_save = static_cast(malloc(get_nonempty_npts_model() * sizeof(double))); #endif #ifdef MPI_ON @@ -1007,7 +1033,7 @@ static void allocate_nonemptymodelcells() { printout("[info] mem_usage: the modelgrid array occupies %.3f MB\n", (get_npts_model() + 1) * sizeof(modelgrid[0]) / 1024. / 1024.); - printout("There are %d modelgrid cells with associated propagation cells\n", nonempty_npts_model); + printout("There are %zu modelgrid cells with associated propagation cells\n", nonempty_npts_model); printout( "[info] mem_usage: NLTE populations for all allocated cells occupy a total of %.3f MB (node shared memory)\n", @@ -1038,7 +1064,7 @@ static void map_2dmodelto3dgrid() int mgi = get_npts_model(); // default to empty unless set // map to 3D Cartesian grid - double pos_mid[3]; + std::array pos_mid{}; for (int d = 0; d < 3; d++) { pos_mid[d] = (get_cellcoordmin(cellindex, d) + (0.5 * wid_init(cellindex, d))); } @@ -1278,16 +1304,16 @@ static auto read_model_columns(std::fstream &fmodel) -> std::tuple std::tuple(malloc((get_npts_model() + 1) * sizeof(double))); // Now read the time (in days) at which the model is specified. - double t_model_days = NAN; + double t_model_days{NAN}; assert_always(get_noncommentline(fmodel, line)); std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; @@ -1362,8 +1388,8 @@ static void read_1d_model() int mgi = 0; while (std::getline(fmodel, line)) { - double vout_kmps = NAN; - double log_rho = NAN; + double vout_kmps{NAN}; + double log_rho{NAN}; int cellnumberin = 0; std::istringstream ssline(line); @@ -1394,7 +1420,7 @@ static void read_1d_model() if (mgi != get_npts_model()) { printout("ERROR in model.txt. Found only %d cells instead of %d expected.\n", mgi - 1, get_npts_model()); - abort(); + std::abort(); } globals::vmax = vout_model[get_npts_model() - 1]; @@ -1415,7 +1441,7 @@ static void read_2d_model() 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; + double t_model_days{NAN}; assert_always(get_noncommentline(fmodel, line)); std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; @@ -1435,9 +1461,9 @@ static void read_2d_model() 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; + float cell_r_in{NAN}; + float cell_z_in{NAN}; + double rho_tmodel{NAN}; std::istringstream ssline(line); assert_always(ssline >> cellnumberin >> cell_r_in >> cell_z_in >> rho_tmodel); @@ -1455,7 +1481,7 @@ static void read_2d_model() if (rho_tmodel < 0) { printout("negative input density %g %d\n", rho_tmodel, mgi); - abort(); + std::abort(); } const bool keepcell = (rho_tmodel > 0); @@ -1474,10 +1500,10 @@ static void read_2d_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()); - abort(); + std::abort(); } - printout("Effectively used model grid cells: %d\n", nonemptymgi); + printout("effectively used model grid cells: %d\n", nonemptymgi); } static void read_3d_model() @@ -1503,7 +1529,7 @@ static void read_3d_model() ncoordgrid[2] = ncoord_model[2]; ngrid = npts_model_in; - double t_model_days = NAN; + double t_model_days{NAN}; assert_always(get_noncommentline(fmodel, line)); std::istringstream(line) >> t_model_days; t_model = t_model_days * DAY; @@ -1530,8 +1556,8 @@ static void read_3d_model() int nonemptymgi = 0; while (std::getline(fmodel, line)) { int cellnumberin = 0; - float cellpos_in[3]; - float rho_model = NAN; + std::array cellpos_in{}; + float rho_model{NAN}; std::istringstream ssline(line); assert_always(ssline >> cellnumberin >> cellpos_in[0] >> cellpos_in[1] >> cellpos_in[2] >> rho_model); @@ -1567,7 +1593,7 @@ static void read_3d_model() if (rho_model < 0) { printout("negative input density %g %d\n", rho_model, mgi); - abort(); + std::abort(); } // in 3D cartesian, cellindex and modelgridindex are interchangeable @@ -1590,7 +1616,7 @@ static void read_3d_model() } if (mgi != npts_model_in) { printout("ERROR in model.txt. Found %d cells instead of %d expected.\n", mgi, npts_model_in); - abort(); + std::abort(); } // assert_always(posmatch_zyx ^ posmatch_xyz); // xor because if both match then probably an infinity occurred @@ -1608,7 +1634,7 @@ static void read_3d_model() } printout("min_den %g [g/cm3]\n", min_den); - printout("Effectively used model grid cells: %d\n", nonemptymgi); + printout("effectively used model grid cells: %d\n", nonemptymgi); } static void calc_modelinit_totmassradionuclides() { @@ -1644,7 +1670,7 @@ static void calc_modelinit_totmassradionuclides() { 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(); + std::abort(); } const double mass_in_shell = get_rho_tmin(mgi) * cellvolume; @@ -1690,8 +1716,12 @@ void read_ejecta_model() { } default: { - printout("Unknown model type. Abort.\n"); - abort(); + if constexpr (TESTMODE) { + printout("ERROR: Unknown model type %d\n", get_model_type()); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); + } } } @@ -1722,10 +1752,6 @@ static void read_grid_restart_data(const int timestep) { assert_always(fscanf(gridsave_file, "%d ", &nprocs_in) == 1); assert_always(nprocs_in == globals::nprocs); - int nthreads_in = -1; - assert_always(fscanf(gridsave_file, "%d ", &nthreads_in) == 1); - assert_always(nthreads_in == get_num_threads()); - 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 ", @@ -1752,10 +1778,10 @@ static void read_grid_restart_data(const int timestep) { float W = 0.; float T_J = 0.; int thick = 0; - double rpkt_emiss = 0.; + double dep_estimator_gamma = 0.; 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); + &dep_estimator_gamma, &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"); @@ -1767,22 +1793,20 @@ static void read_grid_restart_data(const int timestep) { assert_always(T_e >= 0.); assert_always(W >= 0.); assert_always(T_J >= 0.); - assert_always(rpkt_emiss >= 0.); + assert_always(dep_estimator_gamma >= 0.); set_TR(mgi, T_R); set_Te(mgi, T_e); set_W(mgi, W); set_TJ(mgi, T_J); modelgrid[mgi].thick = thick; - globals::rpkt_emiss[mgi] = rpkt_emiss; + globals::dep_estimator_gamma[nonemptymgi] = dep_estimator_gamma; if constexpr (USE_LUT_PHOTOION) { - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < (get_nions(element) - 1); ion++) { - const int estimindex = get_ionestimindex(mgi, element, ion); - assert_always(fscanf(gridsave_file, " %la %la", &globals::corrphotoionrenorm[estimindex], - &globals::gammaestimator[estimindex]) == 2); - } + for (int i = 0; i < globals::nbfcontinua_ground; i++) { + const int estimindex = nonemptymgi * globals::nbfcontinua_ground + i; + assert_always(fscanf(gridsave_file, " %la %la", &globals::corrphotoionrenorm[estimindex], + &globals::gammaestimator[estimindex]) == 2); } } } @@ -1798,14 +1822,13 @@ void write_grid_restart_data(const int timestep) { char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "gridsave_ts%d.tmp", timestep); - const time_t sys_time_start_write_restart = time(nullptr); + const auto sys_time_start_write_restart = std::time(nullptr); printout("Write grid restart data to %s...", filename); FILE *gridsave_file = fopen_required(filename, "w"); 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::ntimesteps; nts++) { fprintf(gridsave_file, "%la %la %la %la %la %la %la %la %la %la %la %la %la %la %la %d ", @@ -1816,7 +1839,7 @@ void write_grid_restart_data(const int timestep) { 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()); + globals::timesteps[nts].pellet_decays); } fprintf(gridsave_file, "%d ", timestep); @@ -1824,17 +1847,15 @@ void write_grid_restart_data(const int timestep) { for (int nonemptymgi = 0; nonemptymgi < get_nonempty_npts_model(); nonemptymgi++) { const int mgi = grid::get_mgi_of_nonemptymgi(nonemptymgi); - assert_always(globals::rpkt_emiss[mgi] >= 0.); + assert_always(globals::dep_estimator_gamma[nonemptymgi] >= 0.); 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); + modelgrid[mgi].thick, globals::dep_estimator_gamma[nonemptymgi], modelgrid[mgi].nne, modelgrid[mgi].nnetot); if constexpr (USE_LUT_PHOTOION) { - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < (get_nions(element) - 1); ion++) { - const int estimindex = get_ionestimindex(mgi, element, ion); - fprintf(gridsave_file, " %la %la", globals::corrphotoionrenorm[estimindex], - globals::gammaestimator[estimindex]); - } + for (int i = 0; i < globals::nbfcontinua_ground; i++) { + const int estimindex = nonemptymgi * globals::nbfcontinua_ground + i; + fprintf(gridsave_file, " %la %la", globals::corrphotoionrenorm[estimindex], + globals::gammaestimator[estimindex]); } } fprintf(gridsave_file, "\n"); @@ -1845,7 +1866,7 @@ void write_grid_restart_data(const int timestep) { nonthermal::write_restart_data(gridsave_file); nltepop_write_restart_data(gridsave_file); fclose(gridsave_file); - printout("done in %ld seconds.\n", time(nullptr) - sys_time_start_write_restart); + printout("done in %ld seconds.\n", std::time(nullptr) - sys_time_start_write_restart); } static void assign_initial_temperatures() @@ -1907,19 +1928,17 @@ static void setup_nstart_ndo() { const int nprocesses = globals::nprocs; const int npts_nonempty = get_nonempty_npts_model(); const int min_nonempty_perproc = npts_nonempty / nprocesses; // integer division, minimum non-empty cells per process - const int n_leftover = npts_nonempty - nprocesses * min_nonempty_perproc; + const int n_remainder = npts_nonempty % nprocesses; maxndo = 0; - ranks_nstart = std::vector(nprocesses); - ranks_ndo = std::vector(nprocesses); - ranks_ndo_nonempty = std::vector(nprocesses); + ranks_nstart.resize(nprocesses); + ranks_ndo.resize(nprocesses); + ranks_ndo_nonempty.resize(nprocesses); // begin with no cell assignments - for (int r = 0; r < nprocesses; r++) { - ranks_nstart[r] = 0; - ranks_ndo[r] = 0; - ranks_ndo_nonempty[r] = 0; - } + std::ranges::fill(ranks_nstart, 0); + std::ranges::fill(ranks_ndo, 0); + std::ranges::fill(ranks_ndo_nonempty, 0); if (nprocesses >= get_npts_model()) { // for convenience, rank == mgi when there is at least one rank per cell @@ -1937,7 +1956,7 @@ static void setup_nstart_ndo() { int rank = 0; for (int mgi = 0; mgi < get_npts_model(); mgi++) { - const int target_nonempty_thisrank = (rank < n_leftover) ? min_nonempty_perproc + 1 : min_nonempty_perproc; + const int target_nonempty_thisrank = (rank < n_remainder) ? min_nonempty_perproc + 1 : min_nonempty_perproc; if ((rank < (nprocesses - 1)) && (ranks_ndo_nonempty[rank] >= target_nonempty_thisrank)) { // current rank has enough non-empty cells, so start assigning cells to the next rank rank++; @@ -2030,12 +2049,12 @@ static void setup_grid_cartesian_3d() assert_always(ncoordgrid[0] == ncoordgrid[2]); ngrid = ncoordgrid[0] * ncoordgrid[1] * ncoordgrid[2]; - cell = static_cast(malloc(ngrid * sizeof(struct gridcell))); + cell = static_cast(malloc(ngrid * sizeof(PropGridCell))); coordlabel[0] = 'X'; coordlabel[1] = 'Y'; coordlabel[2] = 'Z'; - int nxyz[3] = {0, 0, 0}; + std::array nxyz = {0, 0, 0}; for (int n = 0; n < ngrid; n++) { for (int axis = 0; axis < 3; axis++) { assert_always(nxyz[axis] == get_cellcoordpointnum(n, axis)); @@ -2068,7 +2087,7 @@ static void setup_grid_spherical1d() { ncoordgrid[2] = 1; ngrid = ncoordgrid[0] * ncoordgrid[1] * ncoordgrid[2]; - cell = static_cast(malloc(ngrid * sizeof(struct gridcell))); + cell = static_cast(malloc(ngrid * sizeof(PropGridCell))); // direct mapping, cellindex and modelgridindex are the same for (int cellindex = 0; cellindex < get_npts_model(); cellindex++) { @@ -2096,7 +2115,7 @@ static void setup_grid_cylindrical_2d() { ncoordgrid[2] = ncoord_model[2]; ngrid = ncoordgrid[0] * ncoordgrid[1]; - cell = static_cast(malloc(ngrid * sizeof(struct gridcell))); + cell = static_cast(malloc(ngrid * sizeof(PropGridCell))); // direct mapping, cellindex and modelgridindex are the same for (int cellindex = 0; cellindex < get_npts_model(); cellindex++) { @@ -2112,7 +2131,7 @@ static void setup_grid_cylindrical_2d() { } } -void grid_init(int my_rank) +void grid_init(const int my_rank) /// Initialises the propagation grid cells and associates them with modelgrid cells { /// The cells will be ordered by x then y, then z. Call a routine that @@ -2129,7 +2148,7 @@ void grid_init(int my_rank) strcpy(grid_type_name, "cylindrical"); } else { printout("[fatal] grid_init: Error: Unknown grid type. Abort."); - abort(); + std::abort(); } printout("propagation grid: %d-dimensional %s\n", get_ngriddimensions(), grid_type_name); @@ -2166,7 +2185,7 @@ void grid_init(int my_rank) map_2dmodelto3dgrid(); } else { printout("[fatal] grid_init: Error: Unknown density type. Abort."); - abort(); + std::abort(); } allocate_nonemptymodelcells(); @@ -2233,7 +2252,7 @@ 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 { +static auto get_poscoordpointnum(const double pos, const double time, const int axis) -> int { // pos must be position in grid coordinate system, not necessarily xyz if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { @@ -2259,11 +2278,28 @@ static auto get_poscoordpointnum(double pos, double time, int axis) -> int { } } -auto get_cellindex_from_pos(std::span pos, double time) -> int +constexpr static auto get_gridcoords_from_xyz(const std::array pos_xyz) -> std::array { + auto posgridcoord = std::array{}; + 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.; + } + return posgridcoord; +} + +[[nodiscard]] auto get_cellindex_from_pos(const std::array pos, const 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); + auto posgridcoords = get_gridcoords_from_xyz(pos); int cellindex = 0; for (int d = 0; d < get_ngriddimensions(); d++) { cellindex += get_coordcellindexincrement(d) * get_poscoordpointnum(posgridcoords[d], time, d); @@ -2278,9 +2314,10 @@ auto get_cellindex_from_pos(std::span pos, double time) -> int return cellindex; } +template [[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 + const std::array pos, const std::array 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 @@ -2308,8 +2345,8 @@ auto get_cellindex_from_pos(std::span pos, double time) -> int double dist1 = (-b + sqrt(discriminant)) / 2 / a; double dist2 = (-b - sqrt(discriminant)) / 2 / a; - double posfinal1[3] = {0}; - double posfinal2[3] = {0}; + auto posfinal1 = std::array{0.}; + auto posfinal2 = std::array{0.}; for (size_t d = 0; d < pos.size(); d++) { posfinal1[d] = pos[d] + dist1 * dir[d]; @@ -2341,15 +2378,16 @@ auto get_cellindex_from_pos(std::span pos, double time) -> int } } +#if (TESTMODE) 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); } +#endif // negative d means in the reverse direction along the ray // ignore negative d values, and if two are positive then return the smaller one @@ -2362,7 +2400,7 @@ auto get_cellindex_from_pos(std::span pos, double time) -> int if (dist1 < 0) { return dist2; } - return fmin(dist1, dist2); + return std::min(dist1, dist2); } // exactly one intersection @@ -2372,23 +2410,24 @@ auto get_cellindex_from_pos(std::span pos, double time) -> int 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 { +static auto get_coordboundary_distances_cylindrical2d( + const std::array pkt_pos, const std::array pkt_dir, + const std::array pktposgridcoord, const std::array pktvelgridcoord, int cellindex, + const double tstart, + const std::array cellcoordmax) -> std::tuple, std::array> { // 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]}; + std::array d_coordminboundary{}; + std::array d_coordmaxboundary{}; + + const std::array posnoz = {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 std::array dirnoz = {pkt_dir[0] / dirxylen, pkt_dir[1] / dirxylen}; const double r_inner = grid::get_cellcoordmin(cellindex, 0) * tstart / globals::tmin; d_coordminboundary[0] = -1; @@ -2422,17 +2461,18 @@ static auto get_coordboundary_distances_cylindrical2d(std::span ((cellcoordmax[1]) - (pktvelgridcoord[1] * globals::tmin)) * globals::tmin) - tstart; d_coordmaxboundary[1] = CLIGHT_PROP * t_zcoordmaxboundary; + + return {d_coordminboundary, d_coordmaxboundary}; } -[[nodiscard]] auto boundary_distance(std::span dir, std::span pos, - const double tstart, int cellindex, int *snext, - enum cell_boundary *pkt_last_cross) -> double +[[nodiscard]] auto boundary_distance(const std::array dir, const std::array pos, + const double tstart, const int cellindex, + enum cell_boundary *pkt_last_cross) -> std::tuple /// 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.; + return {0., -99}; } } @@ -2442,11 +2482,10 @@ static auto get_coordboundary_distances_cylindrical2d(std::span // 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 + auto cellcoordmax = std::array{0}; + auto pktvelgridcoord = std::array{0}; // dir * CLIGHT_PROP converted from xyz to grid coordinates - get_gridcoords_from_xyz(pos, pktposgridcoord); + const auto pktposgridcoord = get_gridcoords_from_xyz(pos); for (int d = 0; d < ndim; d++) { cellcoordmax[d] = grid::get_cellcoordmax(cellindex, d); @@ -2470,9 +2509,8 @@ static auto get_coordboundary_distances_cylindrical2d(std::span 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}; + const auto negdirections = std::array{COORD0_MIN, COORD1_MIN, COORD2_MIN}; + const auto posdirections = std::array{COORD0_MAX, COORD1_MAX, COORD2_MAX}; // printout("checking inside cell boundary\n"); for (int d = 0; d < ndim; d++) { @@ -2516,14 +2554,13 @@ static auto get_coordboundary_distances_cylindrical2d(std::span 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; + return {0., -99}; } - *snext = cellindex + cellindexstride; + const int 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("[warning] swapping packet cellindex from %d to %d and setting last_cross to %d\n", cellindex, snext, + *pkt_last_cross); + return {0., snext}; } printout("pretending last_cross is %d\n", direction); last_cross = direction; @@ -2531,7 +2568,7 @@ static auto get_coordboundary_distances_cylindrical2d(std::span } } - // printout("pkt_ptr->number %d\n", pkt_ptr->number); + // printout("pkt.number %d\n", pkt.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] * @@ -2539,8 +2576,10 @@ static auto get_coordboundary_distances_cylindrical2d(std::span // 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 + auto d_coordmaxboundary = + std::array{-1}; // distance to reach the cell's upper boundary on each coordinate + auto d_coordminboundary = + std::array{-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 @@ -2558,8 +2597,8 @@ static auto get_coordboundary_distances_cylindrical2d(std::span 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); + std::tie(d_coordminboundary, d_coordmaxboundary) = get_coordboundary_distances_cylindrical2d( + pos, dir, pktposgridcoord, pktvelgridcoord, cellindex, tstart, cellcoordmax); } else if constexpr (GRID_TYPE == GRID_CARTESIAN3D) { // There are six possible boundary crossings. Each of the three @@ -2594,16 +2633,17 @@ static auto get_coordboundary_distances_cylindrical2d(std::span // 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(); + int snext = 0; 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; + snext = -99; } else { *pkt_last_cross = choice; - *snext = cellindex + grid::get_coordcellindexincrement(d); + snext = cellindex + grid::get_coordcellindexincrement(d); } } @@ -2612,58 +2652,40 @@ static auto get_coordboundary_distances_cylindrical2d(std::span choice = negdirections[d]; distance = d_coordminboundary[d]; if (grid::get_cellcoordpointnum(cellindex, d) == 0) { - *snext = -99; + snext = -99; } else { *pkt_last_cross = choice; - *snext = cellindex - grid::get_coordcellindexincrement(d); + 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); + if constexpr (TESTMODE) { + 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); + assert_always(false); + } else { + __builtin_unreachable(); + } } - 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); - } + return {distance, snext}; } } // namespace grid diff --git a/grid.h b/grid.h index 5cc060a2e..934fab85b 100644 --- a/grid.h +++ b/grid.h @@ -1,17 +1,22 @@ +#pragma once #ifndef GRIDINIT_H #define GRIDINIT_H #include +#include #include #include "artisoptions.h" #include "constants.h" +#include "globals.h" #include "packet.h" +#include "sn3d.h" +#include "stats.h" #include "vectors.h" namespace grid { -struct compositionlist_entry { +struct ModelCellElement { float abundance; /// Abundance of the element (by mass!). float *groundlevelpop; /// Pointer to an array of floats which contains the groundlevel populations /// of all included ionisation stages for the element. @@ -19,12 +24,7 @@ struct compositionlist_entry { /// of all included ionisation stages for the element. }; -struct gridcell { - double pos_min[3]; // Initial co-ordinates of inner most corner of cell. - int modelgridindex; -}; - -struct modelgrid_t { +struct ModelGridCell { float Te = -1.; float TR = -1.; float TJ = -1.; @@ -35,26 +35,26 @@ struct modelgrid_t { float rho = -1.; // modelgrid nn_tot float nnetot = -1.; // total electron density (free + bound). - float *initradioabund = nullptr; - float *initmassfracstable = nullptr; - float *elem_meanweight = nullptr; + float *initradioabund{}; + float *initmassfracstable{}; + float *elem_meanweight{}; float initelectronfrac = -1; // Ye: electrons (or protons) per nucleon float initenergyq = 0.; // q: energy in the model at tmin to use with USE_MODEL_INITIAL_ENERGY [erg/g] float ffegrp = 0.; float kappagrey = 0.; - float grey_depth = 0.; /// Grey optical depth to surface of the modelgridcell - /// This is only stored to print it outside the OpenMP loop in update_grid to the - /// estimatorsfile so there is no need to communicate it via MPI so far! - int *elements_uppermost_ion = nullptr; /// Highest ionisation stage which has a decent population for a particular - /// element in a given cell. - compositionlist_entry *composition = nullptr; /// Pointer to an array which contains the time dependent abundances - /// of all included elements and all the groundlevel - /// populations and partition functions for their ions - double *nlte_pops = nullptr; /// Pointer to an array that contains the nlte-level - /// populations for this cell + float grey_depth = 0.; /// Grey optical depth to surface of the modelgridcell + /// This is only stored to print it outside the OpenMP loop in update_grid to the + /// estimatorsfile so there is no need to communicate it via MPI so far! + int *elements_uppermost_ion{}; /// Highest ionisation stage which has a decent population for a particular + /// element in a given cell. + ModelCellElement *composition{}; /// Pointer to an array which contains the time dependent + /// abundances of all included elements and all the groundlevel + /// populations and partition functions for their ions + double *nlte_pops{}; /// Pointer to an array that contains the nlte-level + /// populations for this cell double totalcooling = -1; - double **cooling_contrib_ion = nullptr; + double **cooling_contrib_ion{}; uint_fast8_t thick = 0; }; @@ -71,89 +71,88 @@ constexpr auto get_ngriddimensions() -> int { } } -extern struct modelgrid_t *modelgrid; +inline ModelGridCell *modelgrid{}; -extern int ncoordgrid[3]; -extern int ngrid; -extern char coordlabel[3]; +inline int ngrid{0}; -auto get_elements_uppermost_ion(int modelgridindex, int element) -> int; +[[nodiscard]] auto get_elements_uppermost_ion(int modelgridindex, int element) -> int; void set_elements_uppermost_ion(int modelgridindex, int element, int newvalue); -auto wid_init(int cellindex, int axis) -> double; -auto get_modelcell_assocvolume_tmin(int modelgridindex) -> double; -auto get_gridcell_volume_tmin(int cellindex) -> double; -auto get_cellcoordmax(int cellindex, int axis) -> double; -auto get_cellcoordmin(int cellindex, int axis) -> double; -auto get_cellcoordpointnum(int cellindex, int axis) -> int; -auto get_coordcellindexincrement(int axis) -> int; -auto get_rho_tmin(int modelgridindex) -> float; -auto get_rho(int modelgridindex) -> float; -auto get_nne(int modelgridindex) -> float; -auto get_nnetot(int modelgridindex) -> float; -auto get_ffegrp(int modelgridindex) -> float; +[[nodiscard]] auto wid_init(int cellindex, int axis) -> double; +[[nodiscard]] auto get_modelcell_assocvolume_tmin(int modelgridindex) -> double; +[[nodiscard]] auto get_gridcell_volume_tmin(int cellindex) -> double; +[[nodiscard]] auto get_cellcoordmax(int cellindex, int axis) -> double; +[[nodiscard]] auto get_cellcoordmin(int cellindex, int axis) -> double; +[[nodiscard]] auto get_cellcoordpointnum(int cellindex, int axis) -> int; +[[nodiscard]] auto get_coordcellindexincrement(int axis) -> int; +[[nodiscard]] auto get_rho_tmin(int modelgridindex) -> float; +[[nodiscard]] auto get_rho(int modelgridindex) -> float; +[[nodiscard]] auto get_nne(int modelgridindex) -> float; +[[nodiscard]] auto get_nnetot(int modelgridindex) -> float; +[[nodiscard]] auto get_ffegrp(int modelgridindex) -> float; void set_elem_abundance(int modelgridindex, int element, float newabundance); -auto get_elem_numberdens(int modelgridindex, int element) -> double; -auto get_initelectronfrac(int modelgridindex) -> double; -auto get_initenergyq(int modelgridindex) -> double; -auto get_kappagrey(int modelgridindex) -> float; -auto get_Te(int modelgridindex) -> float; -auto get_TR(int modelgridindex) -> float; -auto get_TJ(int modelgridindex) -> float; -auto get_W(int modelgridindex) -> float; +[[nodiscard]] auto get_elem_numberdens(int modelgridindex, int element) -> double; +[[nodiscard]] auto get_initelectronfrac(int modelgridindex) -> double; +[[nodiscard]] auto get_initenergyq(int modelgridindex) -> double; +[[nodiscard]] auto get_kappagrey(int modelgridindex) -> float; +[[nodiscard]] auto get_Te(int modelgridindex) -> float; +[[nodiscard]] auto get_TR(int modelgridindex) -> float; +[[nodiscard]] auto get_TJ(int modelgridindex) -> float; +[[nodiscard]] auto get_W(int modelgridindex) -> float; void set_nne(int modelgridindex, float nne); -void set_nnetot(int modelgridindex, float x); +void set_nnetot(int modelgridindex, float nnetot); void set_kappagrey(int modelgridindex, float kappagrey); -void set_rho(int modelgridindex, float x); +void set_rho(int modelgridindex, float rho); 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); -auto get_modelinitradioabund(int modelgridindex, int nucindex) -> float; -auto get_stable_initabund(int mgi, int element) -> float; -auto get_element_meanweight(int mgi, int element) -> float; +[[nodiscard]] auto get_modelinitradioabund(int modelgridindex, int nucindex) -> float; +[[nodiscard]] auto get_stable_initabund(int mgi, int element) -> float; +[[nodiscard]] auto get_element_meanweight(int mgi, int element) -> float; void set_element_meanweight(int mgi, int element, float meanweight); -auto get_electronfrac(int modelgridindex) -> double; -auto get_numassociatedcells(int modelgridindex) -> int; -auto get_modelcell_nonemptymgi(int mgi) -> int; -auto get_mgi_of_nonemptymgi(int nonemptymgi) -> int; -auto get_model_type() -> enum gridtypes; +[[nodiscard]] auto get_electronfrac(int modelgridindex) -> double; +[[nodiscard]] auto get_numassociatedcells(int modelgridindex) -> int; +[[nodiscard]] auto get_modelcell_nonemptymgi(int mgi) -> int; +[[nodiscard]] auto get_mgi_of_nonemptymgi(int nonemptymgi) -> int; +[[nodiscard]] auto get_model_type() -> enum gridtypes; void set_model_type(enum gridtypes model_type_value); -auto get_npts_model() -> int; -auto get_nonempty_npts_model() -> int; -auto get_t_model() -> double; -auto get_cell_modelgridindex(int cellindex) -> int; -int get_cellindex_from_pos(std::span pos, double time); +[[nodiscard]] auto get_npts_model() -> int; +[[nodiscard]] auto get_nonempty_npts_model() -> int; +[[nodiscard]] auto get_t_model() -> double; +[[nodiscard]] auto get_cell_modelgridindex(int cellindex) -> int; +[[nodiscard]] auto get_cellindex_from_pos(std::array pos, double time) -> int; void read_ejecta_model(); void write_grid_restart_data(int timestep); -auto get_maxndo() -> int; -auto get_nstart(int rank) -> int; -auto get_ndo(int rank) -> int; -auto get_ndo_nonempty(int rank) -> int; -auto get_totmassradionuclide(int z, int a) -> double; -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); +[[nodiscard]] auto get_maxndo() -> int; +[[nodiscard]] auto get_nstart(int rank) -> int; +[[nodiscard]] auto get_ndo(int rank) -> int; +[[nodiscard]] auto get_ndo_nonempty(int rank) -> int; +[[nodiscard]] auto get_totmassradionuclide(int z, int a) -> double; +[[nodiscard]] auto boundary_distance(std::array dir, std::array pos, double tstart, int cellindex, + enum cell_boundary *pkt_last_cross) -> std::tuple; -static inline auto get_elem_abundance(int modelgridindex, int element) -> float +[[nodiscard]] inline auto get_elem_abundance(int modelgridindex, int element) -> float // mass fraction of an element (all isotopes combined) { 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.; +inline void change_cell(Packet &pkt, const int snext) +/// Routine to take a packet across a boundary. +{ + if (snext >= 0) { + // Just need to update "where". + pkt.where = snext; + } else { + // Then the packet is exiting the grid. We need to record + // where and at what time it leaves the grid. + pkt.escape_type = pkt.type; + pkt.escape_time = pkt.prop_time; + pkt.type = TYPE_ESCAPE; + atomicadd(globals::nesc, 1); + + stats::increment(stats::COUNTER_CELLCROSSINGS); } } diff --git a/input.cc b/input.cc index e886db7a6..2c34e0189 100644 --- a/input.cc +++ b/input.cc @@ -1,34 +1,53 @@ #include "input.h" +#ifdef MPI_ON +#include +#endif + +#include #include #include +#include #include +#include #include +#include #include +#include #include #include +#include +#include #include -#include +#include #include +#include +#include #include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "kpkt.h" +#include "packet.h" #include "ratecoeff.h" #include "sn3d.h" #include "vpkt.h" +namespace { + const int groundstate_index_in = 1; // starting level index in the input files int phixs_file_version = -1; -struct transitions { +struct Transitions { int *to; }; -struct transitiontable_entry { +struct Transition { int lower; int upper; float A; @@ -65,20 +84,22 @@ constexpr 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(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) { +CellCachePhixsTargets *chphixstargetsblock{}; + +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, + size_t *mem_usage_phixs) { if (upperlevel_in >= 0) // file gives photoionisation to a single target state only { int upperlevel = upperlevel_in - groundstate_index_in; assert_always(upperlevel >= 0); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].nphixstargets == 0); globals::elements[element].ions[lowerion].levels[lowerlevel].nphixstargets = 1; - *mem_usage_phixs += sizeof(phixstarget_entry); + *mem_usage_phixs += sizeof(grid::ModelCellElement); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets == nullptr); globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets = - static_cast(calloc(1, sizeof(phixstarget_entry))); + static_cast(calloc(1, sizeof(PhotoionTarget))); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets != nullptr); if (single_level_top_ion && @@ -87,7 +108,7 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint upperlevel = 0; } globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].levelindex = upperlevel; - globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].probability = 1.0; + globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].probability = 1.; } else // upperlevel < 0, indicating that a table of upper levels and their probabilities will follow { int in_nphixstargets = 0; @@ -97,15 +118,15 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint if (!single_level_top_ion || upperion < get_nions(element) - 1) // in case the top ion has nlevelsmax = 1 { globals::elements[element].ions[lowerion].levels[lowerlevel].nphixstargets = in_nphixstargets; - *mem_usage_phixs += in_nphixstargets * sizeof(phixstarget_entry); + *mem_usage_phixs += in_nphixstargets * sizeof(PhotoionTarget); globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets = - static_cast(calloc(in_nphixstargets, sizeof(phixstarget_entry))); + static_cast(calloc(in_nphixstargets, sizeof(PhotoionTarget))); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets != nullptr); double probability_sum = 0.; for (int i = 0; i < in_nphixstargets; i++) { - double phixstargetprobability = NAN; + double phixstargetprobability{NAN}; assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); const int upperlevel = upperlevel_in - groundstate_index_in; assert_always(upperlevel >= 0); @@ -122,19 +143,19 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint } else // file has table of target states and probabilities but our top ion is limited to one level { globals::elements[element].ions[lowerion].levels[lowerlevel].nphixstargets = 1; - *mem_usage_phixs += sizeof(phixstarget_entry); + *mem_usage_phixs += sizeof(PhotoionTarget); globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets = - static_cast(calloc(1, sizeof(phixstarget_entry))); + static_cast(calloc(1, sizeof(PhotoionTarget))); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets != nullptr); for (int i = 0; i < in_nphixstargets; i++) { - double phixstargetprobability = NAN; + double phixstargetprobability{NAN}; assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); } // send it to the ground state of the top ion globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].levelindex = 0; - globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].probability = 1.0; + globals::elements[element].ions[lowerion].levels[lowerlevel].phixstargets[0].probability = 1.; } } @@ -143,8 +164,7 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint /// belong to the topmost ion included. /// Rate coefficients are only available for ionising levels. // also need (levelenergy < ionpot && ...)? - if (lowerion < get_nions(element) - 1) /// thats only an option for pure LTE && level < TAKE_N_BFCONTINUA) - { + if (lowerion < get_nions(element) - 1) { for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, lowerion, lowerlevel); phixstargetindex++) { const int upperlevel = get_phixsupperlevel(element, lowerion, lowerlevel, phixstargetindex); @@ -159,17 +179,6 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint static_cast(calloc(globals::NPHIXSPOINTS, sizeof(float))); assert_always(globals::elements[element].ions[lowerion].levels[lowerlevel].photoion_xs != nullptr); - if (phixs_threshold_ev > 0) { - globals::elements[element].ions[lowerion].levels[lowerlevel].phixs_threshold = phixs_threshold_ev * EV; - } else { - if (get_nphixstargets(element, lowerion, lowerlevel) > 0) { - const int lowestupperlevel = get_phixsupperlevel(element, lowerion, lowerlevel, 0); - const double calced_phixs_threshold = - (epsilon(element, upperion, lowestupperlevel) - epsilon(element, lowerion, lowerlevel)); - globals::elements[element].ions[lowerion].levels[lowerlevel].phixs_threshold = calced_phixs_threshold; - } - } - if (phixs_file_version == 1) { assert_always(get_nphixstargets(element, lowerion, lowerlevel) == 1); assert_always(get_phixsupperlevel(element, lowerion, lowerlevel, 0) == 0); @@ -214,7 +223,7 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint free(phixstable); } else { for (int i = 0; i < globals::NPHIXSPOINTS; i++) { - float phixs = NAN; + float phixs{NAN}; assert_always(phixsfile >> phixs); assert_always(phixs >= 0); @@ -228,12 +237,12 @@ static void read_phixs_data_table(std::fstream &phixsfile, const int nphixspoint // nbfcontinua++; // printout("[debug] element %d, ion %d, level %d: phixs exists %g\n",element,lowerion,lowerlevel,phixs*1e-18); globals::nbfcontinua += get_nphixstargets(element, lowerion, lowerlevel); - if (lowerlevel < get_nlevels_groundterm(element, lowerion)) { - globals::nbfcontinua_ground += get_nphixstargets(element, lowerion, lowerlevel); + if (lowerlevel == 0 && get_nphixstargets(element, lowerion, lowerlevel) > 0) { + globals::nbfcontinua_ground++; } } -static void read_phixs_data(const int phixs_file_version) { +void read_phixs_data(const int phixs_file_version) { size_t mem_usage_phixs = 0; printout("readin phixs data from %s\n", phixsdata_filenames[phixs_file_version]); @@ -266,7 +275,7 @@ static void read_phixs_data(const int phixs_file_version) { int upperlevel_in = -1; int lowerionstage = -1; int lowerlevel_in = -1; - double phixs_threshold_ev = -1; + double phixs_threshold_ev = -1; // currently just ignored, and epilson is used instead while (true) { int nphixspoints_inputtable = 0; std::string phixsline; @@ -303,7 +312,7 @@ static void read_phixs_data(const int phixs_file_version) { /// 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(phixsfile, nphixspoints_inputtable, element, lowerion, lowerlevel, upperion, - upperlevel_in, phixs_threshold_ev, &mem_usage_phixs); + upperlevel_in, &mem_usage_phixs); skip_this_phixs_table = false; } @@ -317,7 +326,7 @@ static void read_phixs_data(const int phixs_file_version) { int nphixstargets = 0; assert_always(phixsfile >> nphixstargets); for (int i = 0; i < nphixstargets; i++) { - double phixstargetprobability = NAN; + double phixstargetprobability{NAN}; assert_always(phixsfile >> upperlevel_in >> phixstargetprobability); } } @@ -337,19 +346,18 @@ static void read_phixs_data(const int phixs_file_version) { printout("[info] mem_usage: photoionisation tables occupy %.3f MB\n", mem_usage_phixs / 1024. / 1024.); } -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); +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, Transitions *transitions) { + const ptrdiff_t nlevels_used = std::min(nlevels, nlevelsmax); // each level contains 0..level elements. seriess sum of 1 + 2 + 3 + 4 + ... + nlevels_used is used here - const int transitblocksize = nlevels_used * (nlevels_used + 1) / 2; + const ptrdiff_t transitblocksize = nlevels_used * (nlevels_used + 1) / 2; transitions[0].to = static_cast(malloc(transitblocksize * sizeof(int))); - int transitionblockindex = 0; + ptrdiff_t transitionblockindex = 0; for (int level = 0; level < nlevels; level++) { int levelindex_in = 0; - double levelenergy = NAN; - double statweight = NAN; + double levelenergy{NAN}; + double statweight{NAN}; int ntransitions = 0; std::string line; assert_always(get_noncommentline(adata, line)); @@ -363,10 +371,8 @@ static void read_ion_levels(std::fstream &adata, const int element, const int io // if (level == 0 && ion == 0) energyoffset = levelenergy; globals::elements[element].ions[ion].levels[level].stat_weight = statweight; assert_always(statweight > 0.); - /// Moved to the section with ionising levels below - // globals::elements[element].ions[ion].levels[level].cont_index = cont_index; - // cont_index--; - /// Initialise the metastable flag to true. Set it to false if a downward transition exists. + + // set the metastable flag to true until we find a a downward transition globals::elements[element].ions[ion].levels[level].metastable = true; // globals::elements[element].ions[ion].levels[level].main_qn = mainqn; @@ -374,8 +380,7 @@ static void read_ion_levels(std::fstream &adata, const int element, const int io /// is below the ionization potential and the level doesn't /// belong to the topmost ion included. /// Rate coefficients are only available for ionising levels. - if (levelenergy < ionpot && ion < nions - 1) /// thats only an option for pure LTE && level < TAKE_N_BFCONTINUA) - { + if (levelenergy < ionpot && ion < nions - 1) { globals::elements[element].ions[ion].ionisinglevels++; } @@ -386,10 +391,8 @@ static void read_ion_levels(std::fstream &adata, const int element, const int io transitionblockindex += level; assert_always((transitionblockindex + level) < transitblocksize); - /// initialize number of downward transitions to zero set_ndowntrans(element, ion, level, 0); - /// initialize number of upward transitions to zero set_nuptrans(element, ion, level, 0); } else { // globals::elements[element].ions[ion].levels[nlevelsmax - 1].stat_weight += statweight; @@ -397,10 +400,9 @@ static void read_ion_levels(std::fstream &adata, const int element, const int io } } -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) { +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(); @@ -487,12 +489,10 @@ static void read_ion_transitions(std::fstream &ftransitiondata, const int tottra } } -static void add_transitions_to_unsorted_linelist(const int element, const int ion, const int nlevelsmax, - const std::vector &transitiontable, - struct transitions *transitions, int *lineindex, - std::vector &temp_linelist) { +void add_transitions_to_unsorted_linelist(const int element, const int ion, const int nlevelsmax, + const std::vector &transitiontable, Transitions *transitions, + int *lineindex, std::vector &temp_linelist) { const int lineindex_initial = *lineindex; - const auto tottransitions = transitiontable.size(); size_t totupdowntrans = 0; // pass 0 to get transition counts of each level // pass 1 to allocate and fill transition arrays @@ -500,25 +500,21 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io *lineindex = lineindex_initial; if (pass == 1) { int alltransindex = 0; - struct level_transition *alltransblock = nullptr; + LevelTransition *alltransblock{}; #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); MPI_Win win = MPI_WIN_NULL; - size_t my_rank_trans = totupdowntrans / globals::node_nprocs; - // rank_in_node 0 gets any remainder - if (globals::rank_in_node == 0) { - my_rank_trans += totupdowntrans - (my_rank_trans * globals::node_nprocs); - } + auto [_, my_rank_trans] = get_range_chunk(totupdowntrans, globals::node_nprocs, globals::rank_in_node); - MPI_Aint size = my_rank_trans * sizeof(linelist_entry); - int disp_unit = sizeof(linelist_entry); + auto size = static_cast(my_rank_trans * sizeof(TransitionLine)); + int disp_unit = sizeof(TransitionLine); MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &alltransblock, &win); MPI_Win_shared_query(win, 0, &size, &disp_unit, &alltransblock); #else - alltransblock = static_cast(malloc(totupdowntrans * sizeof(struct level_transition))); + alltransblock = static_cast(malloc(totupdowntrans * sizeof(LevelTransition))); #endif for (int level = 0; level < nlevelsmax; level++) { @@ -538,9 +534,9 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io } totupdowntrans = 0; - for (size_t ii = 0; ii < tottransitions; ii++) { - const int level = transitiontable[ii].upper; - const int targetlevel = transitiontable[ii].lower; + for (const auto &transition : transitiontable) { + const int level = transition.upper; + const int targetlevel = transition.lower; if (pass == 0) { assert_always(targetlevel >= 0); assert_always(level > targetlevel); @@ -571,16 +567,13 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io totupdowntrans += 2; if (pass == 1 && globals::rank_in_node == 0) { - const float A_ul = transitiontable[ii].A; - const float coll_str = transitiontable[ii].coll_str; + const float A_ul = transition.A; + const float coll_str = transition.coll_str; 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 - // %g\n",*lineindex,element,ion,level-i-1,level,nu_trans); - temp_linelist.push_back({ .nu = nu_trans, .einstein_A = A_ul, @@ -599,14 +592,14 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io .einstein_A = static_cast(A_ul), .coll_str = coll_str, .osc_strength = f_ul, - .forbidden = transitiontable[ii].forbidden}; + .forbidden = transition.forbidden}; globals::elements[element].ions[ion].levels[targetlevel].uptrans[nloweruptrans - 1] = { .lineindex = -1, .targetlevelindex = level, .einstein_A = static_cast(A_ul), .coll_str = coll_str, .osc_strength = f_ul, - .forbidden = transitiontable[ii].forbidden}; + .forbidden = transition.forbidden}; } /// This is not a metastable level. @@ -617,8 +610,8 @@ 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 float A_ul = transitiontable[ii].A; - const float coll_str = transitiontable[ii].coll_str; + const float A_ul = transition.A; + const float coll_str = transition.coll_str; 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; @@ -637,7 +630,7 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io "%d, globals::linelist[linelistindex].lowerlevelindex %d\n", temp_linelist[linelistindex].elementindex, temp_linelist[linelistindex].ionindex, temp_linelist[linelistindex].upperlevelindex, temp_linelist[linelistindex].lowerlevelindex); - abort(); + std::abort(); } const int nupperdowntrans = get_ndowntrans(element, ion, level) + 1; @@ -660,7 +653,7 @@ static void add_transitions_to_unsorted_linelist(const int element, const int io #endif } -static auto calculate_nlevels_groundterm(int element, int ion) -> int { +auto calculate_nlevels_groundterm(int element, int ion) -> int { const int nlevels = get_nlevels(element, ion); if (nlevels == 1) { return 1; @@ -701,7 +694,7 @@ static auto calculate_nlevels_groundterm(int element, int ion) -> int { return nlevels_groundterm; } -static void read_atomicdata_files() { +void read_atomicdata_files() { int totaluptrans = 0; int totaldowntrans = 0; @@ -717,9 +710,9 @@ static void read_atomicdata_files() { set_nelements(nelements_in); /// Initialize the linelist - std::vector temp_linelist; + std::vector temp_linelist; - std::vector transitiontable; + std::vector transitiontable; /// temperature to determine relevant ionstages int T_preset = 0; @@ -743,8 +736,8 @@ static void read_atomicdata_files() { int lowermost_ionstage = 0; int uppermost_ionstage = 0; int nlevelsmax_readin = 0; - double abundance = NAN; - double mass_amu = NAN; + double abundance{NAN}; + double mass_amu{NAN}; 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, @@ -763,7 +756,7 @@ static void read_atomicdata_files() { globals::elements[element].uniqueionindexstart = uniqueionindex; /// Initialize the elements ionlist - globals::elements[element].ions = static_cast(calloc(nions, sizeof(ionlist_entry))); + globals::elements[element].ions = static_cast(calloc(nions, sizeof(Ion))); assert_always(globals::elements[element].ions != nullptr); /// now read in data for all ions of the current element. before doing so initialize @@ -790,8 +783,8 @@ static void read_atomicdata_files() { energyoffset += ionpot; } for (int i = 0; i < nlevels; i++) { - double levelenergy = NAN; - double statweight = NAN; + double levelenergy{NAN}; + double statweight{NAN}; int levelindex = 0; int ntransitions = 0; std::string line; @@ -853,20 +846,20 @@ static void read_atomicdata_files() { globals::elements[element].ions[ion].ionstage = ionstage; globals::elements[element].ions[ion].nlevels = nlevelsmax; globals::elements[element].ions[ion].ionisinglevels = 0; - globals::elements[element].ions[ion].maxrecombininglevel = 0; + globals::elements[element].ions[ion].maxrecombininglevel = -1; 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].uniquelevelindexstart = uniquelevelindex; + globals::elements[element].ions[ion].groundcontindex = -1; 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); - globals::elements[element].ions[ion].levels = - static_cast(calloc(nlevelsmax, sizeof(struct levellist_entry))); + globals::elements[element].ions[ion].levels = static_cast(calloc(nlevelsmax, sizeof(EnergyLevel))); assert_always(globals::elements[element].ions[ion].levels != nullptr); - auto *transitions = static_cast(calloc(nlevelsmax, sizeof(struct transitions))); + auto *transitions = static_cast(calloc(nlevelsmax, sizeof(Transitions))); assert_always(transitions != nullptr); read_ion_levels(adata, element, ion, nions, nlevels, nlevelsmax, energyoffset, ionpot, transitions); @@ -905,7 +898,6 @@ static void read_atomicdata_files() { transitiontable.clear(); for (int level = 0; level < nlevelsmax; level++) { - globals::elements[element].ions[ion].levels[level].uniquelevelindex = uniquelevelindex; globals::elements[element].ions[ion].levels[level].nphixstargets = 0; globals::elements[element].ions[ion].levels[level].phixstargets = nullptr; globals::elements[element].ions[ion].levels[level].photoion_xs = nullptr; @@ -930,7 +922,7 @@ static void read_atomicdata_files() { } if (T_preset > 0) { - abort(); + std::abort(); } /// Set up the list of allowed upward transitions for each level @@ -938,37 +930,37 @@ static void read_atomicdata_files() { printout("total downtrans %d\n", totaldowntrans); printout("[info] mem_usage: transition lists occupy %.3f MB (this rank) and %.3f MB (shared on node)\n", - 2 * uniquelevelindex * sizeof(struct level_transition *) / 1024. / 1024., - (totaluptrans + totaldowntrans) * sizeof(struct level_transition) / 1024. / 1024.); + 2 * uniquelevelindex * sizeof(LevelTransition *) / 1024. / 1024., + (totaluptrans + totaldowntrans) * sizeof(LevelTransition) / 1024. / 1024.); if (globals::rank_in_node == 0) { // sort the lineline in descending frequency - std::sort(temp_linelist.begin(), temp_linelist.end(), + std::sort(EXEC_PAR_UNSEQ temp_linelist.begin(), temp_linelist.end(), [](const auto &a, const auto &b) { return static_cast(a.nu > b.nu); }); for (int i = 0; i < globals::nlines - 1; i++) { const double nu = temp_linelist[i].nu; const double nu_next = temp_linelist[i + 1].nu; if (fabs(nu_next - nu) < (1.e-10 * nu)) { - auto *a1 = &temp_linelist[i]; - auto *a2 = &temp_linelist[i + 1]; - - if ((a1->elementindex == a2->elementindex) && (a1->ionindex == a2->ionindex) && - (a1->lowerlevelindex == a2->lowerlevelindex) && (a1->upperlevelindex == a2->upperlevelindex)) { - printout("Duplicate transition line? %s\n", a1->nu == a2->nu ? "nu match exact" : "close to nu match"); - printout("a: Z=%d ionstage %d lower %d upper %d nu %g lambda %g\n", get_atomicnumber(a1->elementindex), - get_ionstage(a1->elementindex, a1->ionindex), a1->lowerlevelindex, a1->upperlevelindex, a1->nu, - 1e8 * CLIGHT / a1->nu); - printout("b: Z=%d ionstage %d lower %d upper %d nu %g lambda %g\n", get_atomicnumber(a2->elementindex), - get_ionstage(a2->elementindex, a2->ionindex), a2->lowerlevelindex, a2->upperlevelindex, a2->nu, - 1e8 * CLIGHT / a2->nu); + const auto &a1 = temp_linelist[i]; + const auto &a2 = temp_linelist[i + 1]; + + if ((a1.elementindex == a2.elementindex) && (a1.ionindex == a2.ionindex) && + (a1.lowerlevelindex == a2.lowerlevelindex) && (a1.upperlevelindex == a2.upperlevelindex)) { + printout("Duplicate transition line? %s\n", a1.nu == a2.nu ? "nu match exact" : "close to nu match"); + printout("a: Z=%d ionstage %d lower %d upper %d nu %g lambda %g\n", get_atomicnumber(a1.elementindex), + get_ionstage(a1.elementindex, a1.ionindex), a1.lowerlevelindex, a1.upperlevelindex, a1.nu, + 1e8 * CLIGHT / a1.nu); + printout("b: Z=%d ionstage %d lower %d upper %d nu %g lambda %g\n", get_atomicnumber(a2.elementindex), + get_ionstage(a2.elementindex, a2.ionindex), a2.lowerlevelindex, a2.upperlevelindex, a2.nu, + 1e8 * CLIGHT / a2.nu); } } } } // create a linelist shared on node and then copy data across, freeing the local copy - struct linelist_entry *nonconstlinelist = nullptr; + TransitionLine *nonconstlinelist{}; #ifdef MPI_ON MPI_Win win = MPI_WIN_NULL; @@ -978,17 +970,17 @@ static void read_atomicdata_files() { my_rank_lines += globals::nlines - (my_rank_lines * globals::node_nprocs); } - MPI_Aint size = my_rank_lines * sizeof(linelist_entry); - int disp_unit = sizeof(linelist_entry); + MPI_Aint size = my_rank_lines * static_cast(sizeof(TransitionLine)); + int disp_unit = sizeof(TransitionLine); MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &nonconstlinelist, &win); MPI_Win_shared_query(win, 0, &size, &disp_unit, &nonconstlinelist); #else - nonconstlinelist = static_cast(malloc(globals::nlines * sizeof(linelist_entry))); + nonconstlinelist = static_cast(malloc(globals::nlines * sizeof(TransitionLine))); #endif if (globals::rank_in_node == 0) { - memcpy(static_cast(nonconstlinelist), temp_linelist.data(), globals::nlines * sizeof(linelist_entry)); + memcpy(static_cast(nonconstlinelist), temp_linelist.data(), globals::nlines * sizeof(TransitionLine)); temp_linelist.clear(); } @@ -998,7 +990,7 @@ static void read_atomicdata_files() { globals::linelist = nonconstlinelist; nonconstlinelist = nullptr; printout("[info] mem_usage: linelist occupies %.3f MB (node shared memory)\n", - globals::nlines * sizeof(struct linelist_entry) / 1024. / 1024); + globals::nlines * sizeof(TransitionLine) / 1024. / 1024); /// Save sorted linelist into a file // if (rank_global == 0) @@ -1018,8 +1010,15 @@ static void read_atomicdata_files() { printout("establishing connection between transitions and sorted linelist...\n"); - time_t const time_start_establish_linelist_connections = time(nullptr); + auto const time_start_establish_linelist_connections = std::time(nullptr); +#ifdef _OPENMP +#pragma omp parallel for schedule(dynamic) +#endif for (lineindex = 0; lineindex < globals::nlines; lineindex++) { + if (lineindex % globals::node_nprocs != globals::rank_in_node) { + continue; + } + const auto &line = globals::linelist[lineindex]; const int element = line.elementindex; const int ion = line.ionindex; @@ -1046,7 +1045,10 @@ static void read_atomicdata_files() { uptrans->lineindex = lineindex; } - printout(" took %ds\n", time(nullptr) - time_start_establish_linelist_connections); + printout(" took %lds\n", std::time(nullptr) - time_start_establish_linelist_connections); +#ifdef MPI_ON + MPI_Barrier(MPI_COMM_WORLD); +#endif for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); @@ -1102,7 +1104,7 @@ static void read_atomicdata_files() { const int phixstargetlevels = get_phixsupperlevel(element, ion - 1, 0, nphixstargets - 1) + 1; if (nlevels_groundterm != phixstargetlevels) { - printout("WARNING: Z=%d ion_stage %d nlevels_groundterm %d phixstargetlevels(ion-1) %d.\n", + printout("WARNING: Z=%d ionstage %d nlevels_groundterm %d phixstargetlevels(ion-1) %d.\n", get_atomicnumber(element), get_ionstage(element, ion), nlevels_groundterm, phixstargetlevels); // if (nlevels_groundterm < phixstargetlevels) // { @@ -1116,10 +1118,11 @@ static void read_atomicdata_files() { } printout("cont_index %d\n", cont_index); + + update_includedionslevels_maxnions(); } -static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcontestimator, int el, int in, - int ll) -> int +auto search_groundphixslist(double nu_edge, int el, int in, int ll) -> int /// Return the closest ground level continuum index to the given edge /// frequency. If the given edge frequency is redder than the reddest /// continuum return -1. @@ -1130,7 +1133,6 @@ static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcont if (nu_edge < globals::groundcont[0].nu_edge) { index = -1; - *index_in_groundlevelcontestimator = -1; } else { int i = 1; int element = -1; @@ -1143,8 +1145,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; - const int level = globals::groundcont[i - 1].level; - if (element == el && ion == in && level == ll) { + if (element == el && ion == in && ll == 0) { index = i - 1; } else { printout( @@ -1152,9 +1153,9 @@ static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcont "bluest ground-level continuum\n", el, in, ll, nu_edge); printout( - "[fatal] search_groundphixslist: bluest ground level continuum is element %d, ion %d, level %d at " + "[fatal] search_groundphixslist: bluest ground level continuum is element %d, ion %d at " "nu_edge %g\n", - element, ion, level, globals::groundcont[i - 1].nu_edge); + element, ion, globals::groundcont[i - 1].nu_edge); printout("[fatal] search_groundphixslist: i %d, nbfcontinua_ground %d\n", i, globals::nbfcontinua_ground); printout( "[fatal] This shouldn't happen, is hoewever possible if there are multiple levels in the adata file at " @@ -1174,48 +1175,39 @@ static auto search_groundphixslist(double nu_edge, int *index_in_groundlevelcont element = globals::groundcont[index].element; ion = globals::groundcont[index].ion; } - *index_in_groundlevelcontestimator = get_uniqueionindex(element, ion); } return index; } -static void setup_cellhistory() { - /// SET UP THE CELL HISTORY - ///====================================================== - /// Stack which holds information about population and other cell specific data - /// ===> move to update_packets - globals::cellhistory = static_cast(malloc(get_max_threads() * sizeof(struct cellhistory))); - assert_always(globals::cellhistory != nullptr); +void setup_cellcache() { + globals::mutex_cellcachemacroatom.resize(get_includedlevels()); -#ifdef _OPENMP -#pragma omp parallel - { -#endif - size_t mem_usage_cellhistory = 0; - mem_usage_cellhistory += sizeof(struct cellhistory); + // const int num_cellcache_slots = get_max_threads(); + const int num_cellcache_slots = 1; + globals::cellcache = static_cast(malloc(num_cellcache_slots * sizeof(CellCache))); + assert_always(globals::cellcache != nullptr); - printout("[info] input: initializing cellhistory for thread %d ...\n", tid); + for (int cellcachenum = 0; cellcachenum < num_cellcache_slots; cellcachenum++) { + size_t mem_usage_cellcache = 0; + mem_usage_cellcache += sizeof(CellCache); - globals::cellhistory[tid].cellnumber = -99; + printout("[info] input: initializing cellcache for thread %d ...\n", cellcachenum); - mem_usage_cellhistory += globals::ncoolingterms * sizeof(double); - globals::cellhistory[tid].cooling_contrib = static_cast(calloc(globals::ncoolingterms, sizeof(double))); + globals::cellcache[cellcachenum].cellnumber = -99; - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < get_nions(element); ion++) { - globals::cellhistory[tid].cooling_contrib[kpkt::get_coolinglistoffset(element, ion)] = COOLING_UNDEFINED; - } - } + const auto ncoolingterms = kpkt::ncoolingterms; + mem_usage_cellcache += ncoolingterms * sizeof(double); + globals::cellcache[cellcachenum].cooling_contrib = static_cast(calloc(ncoolingterms, sizeof(double))); - printout("[info] mem_usage: cellhistory coolinglist contribs for thread %d occupies %.3f MB\n", tid, - globals::ncoolingterms * sizeof(double) / 1024. / 1024.); + printout("[info] mem_usage: cellcache coolinglist contribs for thread %d occupies %.3f MB\n", cellcachenum, + ncoolingterms * sizeof(double) / 1024. / 1024.); - mem_usage_cellhistory += get_nelements() * sizeof(struct chelements); - globals::cellhistory[tid].chelements = - static_cast(malloc(get_nelements() * sizeof(struct chelements))); + mem_usage_cellcache += get_nelements() * sizeof(CellCacheElements); + globals::cellcache[cellcachenum].chelements = + static_cast(malloc(get_nelements() * sizeof(CellCacheElements))); - assert_always(globals::cellhistory[tid].chelements != nullptr); + assert_always(globals::cellcache[cellcachenum].chelements != nullptr); size_t chlevelblocksize = 0; size_t chphixsblocksize = 0; @@ -1224,11 +1216,11 @@ static void setup_cellhistory() { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { const int nlevels = get_nlevels(element, ion); - chlevelblocksize += nlevels * sizeof(struct chlevels); + chlevelblocksize += nlevels * sizeof(CellCacheLevels); for (int level = 0; level < nlevels; level++) { const int nphixstargets = get_nphixstargets(element, ion, level); - chphixsblocksize += nphixstargets * sizeof(chphixstargets_t); + chphixsblocksize += nphixstargets * sizeof(CellCachePhixsTargets); const int ndowntrans = get_ndowntrans(element, ion, level); const int nuptrans = get_nuptrans(element, ion, level); @@ -1237,12 +1229,12 @@ static void setup_cellhistory() { } } assert_always(chlevelblocksize > 0); - globals::cellhistory[tid].ch_all_levels = static_cast(malloc(chlevelblocksize)); - chphixstargets_t *chphixstargetsblock = - chphixsblocksize > 0 ? static_cast(malloc(chphixsblocksize)) : nullptr; - mem_usage_cellhistory += chlevelblocksize + chphixsblocksize; + globals::cellcache[cellcachenum].ch_all_levels = static_cast(malloc(chlevelblocksize)); + chphixstargetsblock = + chphixsblocksize > 0 ? static_cast(malloc(chphixsblocksize)) : nullptr; + mem_usage_cellcache += chlevelblocksize + chphixsblocksize; - mem_usage_cellhistory += chtransblocksize * sizeof(double); + mem_usage_cellcache += chtransblocksize * sizeof(double); double *const chtransblock = chtransblocksize > 0 ? static_cast(malloc(chtransblocksize * sizeof(double))) : nullptr; @@ -1251,28 +1243,28 @@ static void setup_cellhistory() { int chtransindex = 0; for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); - mem_usage_cellhistory += nions * sizeof(struct chions); - globals::cellhistory[tid].chelements[element].chions = - static_cast(malloc(nions * sizeof(struct chions))); - assert_always(globals::cellhistory[tid].chelements[element].chions != nullptr); + mem_usage_cellcache += nions * sizeof(CellCacheIons); + globals::cellcache[cellcachenum].chelements[element].chions = + static_cast(malloc(nions * sizeof(CellCacheIons))); + assert_always(globals::cellcache[cellcachenum].chelements[element].chions != nullptr); for (int ion = 0; ion < nions; ion++) { const int nlevels = get_nlevels(element, ion); - globals::cellhistory[tid].chelements[element].chions[ion].chlevels = - &globals::cellhistory[tid].ch_all_levels[alllevelindex]; + globals::cellcache[cellcachenum].chelements[element].chions[ion].chlevels = + &globals::cellcache[cellcachenum].ch_all_levels[alllevelindex]; assert_always(alllevelindex == get_uniquelevelindex(element, ion, 0)); alllevelindex += nlevels; for (int level = 0; level < nlevels; level++) { - struct chlevels *chlevel = &globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; + CellCacheLevels *chlevel = &globals::cellcache[cellcachenum].chelements[element].chions[ion].chlevels[level]; const int nphixstargets = get_nphixstargets(element, ion, level); chlevel->chphixstargets = chphixsblocksize > 0 ? &chphixstargetsblock[allphixstargetindex] : nullptr; allphixstargetindex += nphixstargets; } for (int level = 0; level < nlevels; level++) { - struct chlevels *chlevel = &globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; + CellCacheLevels *chlevel = &globals::cellcache[cellcachenum].chelements[element].chions[ion].chlevels[level]; const int ndowntrans = get_ndowntrans(element, ion, level); chlevel->sum_epstrans_rad_deexc = &chtransblock[chtransindex]; @@ -1280,14 +1272,14 @@ static void setup_cellhistory() { } for (int level = 0; level < nlevels; level++) { - struct chlevels *chlevel = &globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; + CellCacheLevels *chlevel = &globals::cellcache[cellcachenum].chelements[element].chions[ion].chlevels[level]; const int ndowntrans = get_ndowntrans(element, ion, level); chlevel->sum_internal_down_same = &chtransblock[chtransindex]; chtransindex += ndowntrans; } for (int level = 0; level < nlevels; level++) { - struct chlevels *chlevel = &globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level]; + CellCacheLevels *chlevel = &globals::cellcache[cellcachenum].chelements[element].chions[ion].chlevels[level]; const int nuptrans = get_nuptrans(element, ion, level); chlevel->sum_internal_up_same = &chtransblock[chtransindex]; chtransindex += nuptrans; @@ -1297,28 +1289,22 @@ static void setup_cellhistory() { assert_always(chtransindex == chtransblocksize); assert_always(globals::nbfcontinua >= 0); - globals::cellhistory[tid].ch_allcont_departureratios = + globals::cellcache[cellcachenum].ch_allcont_departureratios = static_cast(malloc(globals::nbfcontinua * sizeof(double))); - mem_usage_cellhistory += globals::nbfcontinua * sizeof(double); + mem_usage_cellcache += globals::nbfcontinua * sizeof(double); - printout("[info] mem_usage: cellhistory for thread %d occupies %.3f MB\n", tid, - mem_usage_cellhistory / 1024. / 1024.); -#ifdef _OPENMP + printout("[info] mem_usage: cellcache for thread %d occupies %.3f MB\n", cellcachenum, + mem_usage_cellcache / 1024. / 1024.); } -#endif } -static void write_bflist_file(int includedphotoiontransitions) { - if (includedphotoiontransitions > 0) { - globals::bflist = static_cast(malloc(includedphotoiontransitions * sizeof(struct bflist_t))); - } else { - globals::bflist = nullptr; - } +void write_bflist_file() { + globals::bflist.resize(globals::nbfcontinua); - FILE *bflist_file = nullptr; + FILE *bflist_file{}; if (globals::rank_global == 0) { bflist_file = fopen_required("bflist.out", "w"); - fprintf(bflist_file, "%d\n", includedphotoiontransitions); + fprintf(bflist_file, "%d\n", globals::nbfcontinua); } int i = 0; for (int element = 0; element < get_nelements(); element++) { @@ -1326,7 +1312,8 @@ static void write_bflist_file(int includedphotoiontransitions) { for (int ion = 0; ion < nions; ion++) { const int nlevels = get_ionisinglevels(element, ion); for (int level = 0; level < nlevels; level++) { - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const int upperionlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); globals::bflist[i].elementindex = element; globals::bflist[i].ionindex = ion; @@ -1339,7 +1326,7 @@ static void write_bflist_file(int includedphotoiontransitions) { const int et = -1 - i; - assert_always(et == get_continuumindex(element, ion, level, upperionlevel)); + assert_always(et == get_emtype_continuum(element, ion, level, upperionlevel)); // check the we don't overload the same packet emission type numbers // as the special values for free-free scattering and not set @@ -1350,99 +1337,39 @@ static void write_bflist_file(int includedphotoiontransitions) { } } } - assert_always(i == includedphotoiontransitions); + assert_always(i == globals::nbfcontinua); if (globals::rank_global == 0) { fclose(bflist_file); } } -static void setup_phixs_list() { +void setup_phixs_list() { // set up the photoionisation transition lists // and temporary gamma/kappa lists for each thread printout("[info] read_atomicdata: number of bfcontinua %d\n", globals::nbfcontinua); printout("[info] read_atomicdata: number of ground-level bfcontinua %d\n", globals::nbfcontinua_ground); - globals::phixslist = static_cast(malloc(get_max_threads() * sizeof(struct phixslist))); - assert_always(globals::phixslist != nullptr); - - /// MK: 2012-01-19 - /// To fix the OpenMP problem on BlueGene machines this parallel section was removed and replaced by - /// a serial loop which intializes the phixslist data structure for all threads in a loop. I'm still - /// not sure why this causes a problem at all and on BlueGene architectures in particular. However, - /// it seems to fix the problem. - // #ifdef _OPENMP - // #pragma omp parallel private(i,element,ion,level,nions,nlevels,epsilon_upper,E_threshold,nu_edge) - // { - // #endif - for (int itid = 0; itid < get_max_threads(); itid++) { - /// Number of ground level bf-continua equals the total number of included ions minus the number - /// of included elements, because the uppermost ionisation stages can't ionise. - if ((USE_LUT_PHOTOION || USE_LUT_BFHEATING) && globals::nbfcontinua_ground > 0) { - globals::phixslist[itid].groundcont_gamma_contr = - static_cast(malloc(globals::nbfcontinua_ground * sizeof(double))); - assert_always(globals::phixslist[itid].groundcont_gamma_contr != nullptr); - - for (int groundcontindex = 0; groundcontindex < globals::nbfcontinua_ground; groundcontindex++) { - globals::phixslist[itid].groundcont_gamma_contr[groundcontindex] = 0.; - } - } else { - globals::phixslist[itid].groundcont_gamma_contr = nullptr; - } - - if (globals::nbfcontinua > 0) { - 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))); - assert_always(globals::phixslist[itid].gamma_contr != nullptr); - } - - for (int allcontindex = 0; allcontindex < globals::nbfcontinua; allcontindex++) { - 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].chi_bf_sum = nullptr; - globals::phixslist[itid].gamma_contr = nullptr; - } - - printout("[info] mem_usage: phixslist[tid].chi_bf_contr for thread %d occupies %.3f MB\n", itid, - globals::nbfcontinua * sizeof(double) / 1024. / 1024.); - } - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - globals::groundcont = - static_cast(malloc(globals::nbfcontinua_ground * sizeof(struct groundphixslist))); + globals::groundcont = static_cast(malloc(globals::nbfcontinua_ground * sizeof(GroundPhotoion))); assert_always(globals::groundcont != nullptr); - } - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { int groundcontindex = 0; for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { - const int nlevels_groundterm = get_nlevels_groundterm(element, ion); - for (int level = 0; level < nlevels_groundterm; level++) { - const int nphixstargets = get_nphixstargets(element, ion, level); - for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { - // const int upperlevel = get_phixsupperlevel(element, ion,level, 0); - // const double E_threshold = epsilon(element, ion + 1, upperlevel) - epsilon(element, ion, level); - const double E_threshold = get_phixs_threshold(element, ion, level, phixstargetindex); - const double nu_edge = E_threshold / H; - assert_always(groundcontindex < globals::nbfcontinua_ground); - globals::groundcont[groundcontindex].element = element; - globals::groundcont[groundcontindex].ion = ion; - globals::groundcont[groundcontindex].level = level; - globals::groundcont[groundcontindex].nu_edge = nu_edge; - globals::groundcont[groundcontindex].phixstargetindex = phixstargetindex; - groundcontindex++; - } + const int level = 0; + const int nphixstargets = get_nphixstargets(element, ion, level); + if (nphixstargets == 0) { + continue; } + const double E_threshold = get_phixs_threshold(element, ion, level, 0); + const double nu_edge = E_threshold / H; + assert_always(groundcontindex < globals::nbfcontinua_ground); + + globals::groundcont[groundcontindex] = {.nu_edge = nu_edge, .element = element, .ion = ion}; + + groundcontindex++; } } assert_always(groundcontindex == globals::nbfcontinua_ground); @@ -1451,15 +1378,24 @@ static void setup_phixs_list() { } auto *nonconstallcont = - static_cast(malloc(globals::nbfcontinua * sizeof(struct fullphixslist))); + static_cast(malloc(globals::nbfcontinua * sizeof(FullPhotoionTransition))); printout("[info] mem_usage: photoionisation list occupies %.3f MB\n", - globals::nbfcontinua * (sizeof(fullphixslist)) / 1024. / 1024.); + globals::nbfcontinua * (sizeof(FullPhotoionTransition)) / 1024. / 1024.); size_t nbftables = 0; int allcontindex = 0; - globals::bfestimcount = 0; for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions - 1; ion++) { + if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { + globals::elements[element].ions[ion].groundcontindex = std::distance( + globals::groundcont, std::find_if(globals::groundcont, globals::groundcont + globals::nbfcontinua_ground, + [=](const auto &groundcont) { + return (groundcont.element == element) && (groundcont.ion == ion); + })); + if (globals::elements[element].ions[ion].groundcontindex >= globals::nbfcontinua_ground) { + globals::elements[element].ions[ion].groundcontindex = -1; + } + } const int nlevels = get_ionisinglevels(element, ion); for (int level = 0; level < nlevels; level++) { const int nphixstargets = get_nphixstargets(element, ion, level); @@ -1469,10 +1405,7 @@ static void setup_phixs_list() { } for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { - // const int upperlevel = get_phixsupperlevel(element, ion,level, 0); - // const double E_threshold = epsilon(element, ion + 1, upperlevel) - epsilon(element, ion, level); - const double E_threshold = get_phixs_threshold(element, ion, level, phixstargetindex); - const double nu_edge = E_threshold / H; + const double nu_edge = get_phixs_threshold(element, ion, level, phixstargetindex) / H; assert_always(allcontindex < globals::nbfcontinua); nonconstallcont[allcontindex].nu_edge = nu_edge; @@ -1483,45 +1416,55 @@ static void setup_phixs_list() { nonconstallcont[allcontindex].probability = get_phixsprobability(element, ion, level, phixstargetindex); nonconstallcont[allcontindex].upperlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); - if (LEVEL_HAS_BFEST(get_atomicnumber(element), get_ionstage(element, ion), level)) { - nonconstallcont[allcontindex].bfestimindex = globals::bfestimcount; - globals::bfestimcount++; - } else { - nonconstallcont[allcontindex].bfestimindex = -1; - } - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - int index_in_groundlevelcontestimator = 0; - nonconstallcont[allcontindex].index_in_groundphixslist = - search_groundphixslist(nu_edge, &index_in_groundlevelcontestimator, element, ion, level); + const double nu_edge_target0 = get_phixs_threshold(element, ion, level, 0) / H; + const auto groundcontindex = search_groundphixslist(nu_edge_target0, element, ion, level); + nonconstallcont[allcontindex].index_in_groundphixslist = groundcontindex; - globals::elements[element].ions[ion].levels[level].closestgroundlevelcont = - index_in_groundlevelcontestimator; + globals::elements[element].ions[ion].levels[level].closestgroundlevelcont = groundcontindex; } allcontindex++; } } } } - printout("[info] BF estimators activated for %d photoionisation transitions\n", globals::bfestimcount); assert_always(allcontindex == globals::nbfcontinua); assert_always(globals::nbfcontinua >= 0); // was initialised as -1 before startup + globals::bfestimcount = 0; if (globals::nbfcontinua > 0) { // indicies above were temporary only. continum index should be to the sorted list std::sort(nonconstallcont, nonconstallcont + globals::nbfcontinua, [](const auto &a, const auto &b) { return static_cast(a.nu_edge < b.nu_edge); }); - globals::allcont_nu_edge = static_cast(malloc(globals::nbfcontinua * sizeof(double))); + globals::bfestim_nu_edge.clear(); + for (int i = 0; i < globals::nbfcontinua; i++) { + auto &cont = nonconstallcont[i]; + if (DETAILED_BF_ESTIMATORS_ON && + LEVEL_HAS_BFEST(get_atomicnumber(cont.element), get_ionstage(cont.element, cont.ion), cont.level)) { + cont.bfestimindex = globals::bfestimcount; + globals::bfestim_nu_edge.push_back(cont.nu_edge); + globals::bfestimcount++; + } else { + cont.bfestimindex = -1; + } + } + + globals::allcont_nu_edge.resize(globals::nbfcontinua, 0.); + globals::bfestim_nu_edge.shrink_to_fit(); + assert_always(globals::bfestimcount == std::ssize(globals::bfestim_nu_edge)); + } + printout("[info] bound-free estimators track bfestimcount %d photoionisation transitions\n", globals::bfestimcount); + if (globals::nbfcontinua > 0) { // copy the photoionisation tables into one contiguous block of memory #ifdef MPI_ON - float *allphixsblock = nullptr; + float *allphixsblock{}; MPI_Win win_allphixsblock = MPI_WIN_NULL; auto size = static_cast((globals::rank_in_node == 0) ? nbftables * globals::NPHIXSPOINTS * sizeof(float) : 0); - int disp_unit = sizeof(linelist_entry); + int disp_unit = sizeof(TransitionLine); MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &allphixsblock, &win_allphixsblock); MPI_Win_shared_query(win_allphixsblock, MPI_PROC_NULL, &size, &disp_unit, &allphixsblock); @@ -1572,11 +1515,9 @@ static void setup_phixs_list() { setup_photoion_luts(); } -static void read_atomicdata() { +void read_atomicdata() { read_atomicdata_files(); - printout("included ions %d\n", get_includedions()); - /// INITIALISE THE ABSORPTION/EMISSION COUNTERS ARRAYS if constexpr (RECORD_LINESTAT) { globals::ecounter = static_cast(malloc(globals::nlines * sizeof(int))); @@ -1588,13 +1529,10 @@ static void read_atomicdata() { kpkt::setup_coolinglist(); - setup_cellhistory(); + setup_cellcache(); /// Printout some information about the read-in model atom - update_includedions_maxnions(); - - int includedlevels = 0; int includedionisinglevels = 0; int includedboundboundtransitions = 0; int includedphotoiontransitions = 0; @@ -1602,7 +1540,7 @@ static void read_atomicdata() { printout("----------------------------------\n"); for (int element = 0; element < get_nelements(); element++) { printout("[input] element %d (Z=%2d %s)\n", element, get_atomicnumber(element), - decay::get_elname(get_atomicnumber(element))); + decay::get_elname(get_atomicnumber(element)).c_str()); const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { int ion_photoiontransitions = 0; @@ -1613,12 +1551,11 @@ static void read_atomicdata() { } printout( - "[input] ion_stage %d: %4d levels (%d in groundterm, %4d ionising) %7d lines %6d bf transitions " + "[input] ionstage %d: %4d levels (%d in groundterm, %4d ionising) %7d lines %6d bf transitions " "(epsilon_ground: %7.2f eV)\n", get_ionstage(element, ion), get_nlevels(element, ion), get_nlevels_groundterm(element, ion), get_ionisinglevels(element, ion), ion_bbtransitions, ion_photoiontransitions, epsilon(element, ion, 0) / EV); - includedlevels += get_nlevels(element, ion); includedionisinglevels += get_ionisinglevels(element, ion); includedphotoiontransitions += ion_photoiontransitions; includedboundboundtransitions += ion_bbtransitions; @@ -1628,9 +1565,9 @@ static void read_atomicdata() { assert_always(globals::nlines == includedboundboundtransitions); printout("[input] in total %d ions, %d levels (%d ionising), %d lines, %d photoionisation transitions\n", - get_includedions(), includedlevels, includedionisinglevels, globals::nlines, globals::nbfcontinua); + get_includedions(), get_includedlevels(), includedionisinglevels, globals::nlines, globals::nbfcontinua); - write_bflist_file(globals::nbfcontinua); + write_bflist_file(); setup_phixs_list(); @@ -1650,10 +1587,14 @@ static void read_atomicdata() { globals::elements[element].ions[ion].first_nlte = globals::total_nlte_levels; const int nlevels = get_nlevels(element, ion); int fullnlteexcitedlevelcount = 0; + bool found_lte_only_level = false; for (int level = 1; level < nlevels; level++) { if (is_nlte(element, ion, level)) { fullnlteexcitedlevelcount++; globals::total_nlte_levels++; + assert_always(found_lte_only_level == false); // NLTE levels must be consecutive + } else { + found_lte_only_level = true; } } globals::elements[element].ions[ion].nlevels_nlte = fullnlteexcitedlevelcount; @@ -1669,7 +1610,7 @@ static void read_atomicdata() { 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, + printout("[input] element %2d Z=%2d ionstage %2d has %5d NLTE excited levels%s. Starting at %d\n", element, get_atomicnumber(element), get_ionstage(element, ion), fullnlteexcitedlevelcount, has_superlevel ? " plus a superlevel" : "", globals::elements[element].ions[ion].first_nlte); } @@ -1679,6 +1620,8 @@ static void read_atomicdata() { printout("[input] Total NLTE levels: %d, of which %d are superlevels\n", globals::total_nlte_levels, n_super_levels); } +} // anonymous namespace + void input(int rank) /// To govern the input. For now hardwire everything. { @@ -1689,7 +1632,7 @@ void input(int rank) if (globals::n_titer > 1) { #ifndef DO_TITER printout("[fatal] input: n_titer > 1, but DO_TITER not defined ... abort\n"); - abort(); + std::abort(); #endif } else if (globals::n_titer == 1) { #ifdef DO_TITER @@ -1697,7 +1640,7 @@ void input(int rank) #endif } else { printout("[fatal] input: no valid value for n_titer selected\n"); - abort(); + std::abort(); } /// Read in parameters from input.txt @@ -1711,7 +1654,7 @@ void input(int rank) read_atomicdata(); #ifdef MPI_ON - const time_t time_before_barrier = time(nullptr); + const auto time_before_barrier = std::time(nullptr); printout("barrier after read_atomicdata(): time before barrier %d, ", static_cast(time_before_barrier)); MPI_Barrier(MPI_COMM_WORLD); printout("time after barrier %d (waited %d seconds)\n", static_cast(time(nullptr)), @@ -1746,34 +1689,38 @@ void read_parameterfile(int rank) assert_always(get_noncommentline(file, line)); - int64_t zseed_input = -1; - std::istringstream(line) >> zseed_input; + long long int pre_zseed = -1; + std::istringstream(line) >> pre_zseed; -#ifdef _OPENMP + if (pre_zseed > 0) { + printout("using input.txt specified random number seed of %lld\n", pre_zseed); + } else { + pre_zseed = std::random_device{}(); +#ifdef MPI_ON + // broadcast randomly-generated seed from rank 0 to all ranks + MPI_Bcast(&pre_zseed, 1, MPI_LONG_LONG_INT, 0, MPI_COMM_WORLD); +#endif + printout("randomly-generated random number seed is %lld\n", pre_zseed); + } + +#if defined(_OPENMP) && !defined(GPU_ON) #pragma omp parallel - { #endif - uint64_t pre_zseed{}; - if (zseed_input > 0) { - pre_zseed = static_cast(zseed_input); // random number seed - printout("using input.txt specified random number seed of %lu\n", pre_zseed); - } else { - pre_zseed = std::random_device{}(); - printout("randomly-generated random number seed is %lu\n", pre_zseed); - } + { /// 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 uint64_t zseed = pre_zseed + static_cast(13 * (rank * get_num_threads() + tid)); - printout("rank %d: thread %d has zseed %lu\n", rank, tid, zseed); - rng_init(zseed); - /// call it a few times + const auto tid = get_thread_num(); + auto rngseed = pre_zseed + static_cast(13 * (rank * get_max_threads() + tid)); + stdrng.seed(rngseed); + printout("rank %d: thread %d has rngseed %lld\n", rank, tid, rngseed); + printout("rng is a std::mt19937 generator\n"); + + // call it a few times for (int n = 0; n < 100; n++) { rng_uniform(); } -#ifdef _OPENMP } -#endif assert_always(get_noncommentline(file, line)); std::istringstream(line) >> globals::ntimesteps; // number of time steps @@ -1823,7 +1770,7 @@ void read_parameterfile(int rank) assert_always(get_noncommentline(file, line)); std::istringstream(line) >> globals::gamma_kappagrey; // use grey opacity for gammas? - float syn_dir_in[3]; + std::array syn_dir_in{-1.}; assert_always(get_noncommentline(file, line)); std::istringstream(line) >> syn_dir_in[0] >> syn_dir_in[1] >> syn_dir_in[2]; // components of syn_dir @@ -1869,7 +1816,7 @@ void read_parameterfile(int rank) /// Wavelength (in Angstroms) at which the parameterisation of the radiation field /// switches from the nebular approximation to LTE. - float dum2 = NAN; + float dum2{NAN}; assert_always(get_noncommentline(file, line)); std::istringstream(line) >> dum2; // free parameter for calculation of rho_crit globals::nu_rfcut = CLIGHT / (dum2 * 1e-8); @@ -1938,9 +1885,10 @@ 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::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); + int n_kpktdiffusion_timesteps{0}; + float kpktdiffusion_timescale{0.}; + std::istringstream(line) >> kpktdiffusion_timescale >> n_kpktdiffusion_timesteps; + kpkt::set_kpktdiffusion(kpktdiffusion_timescale, n_kpktdiffusion_timesteps); file.close(); @@ -2039,7 +1987,7 @@ void time_init() /// 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::timesteps = std::make_unique(globals::ntimesteps + 1); + globals::timesteps.resize(globals::ntimesteps + 1); /// Now set the individual time steps switch (TIMESTEP_SIZE_METHOD) { @@ -2185,7 +2133,7 @@ void time_init() 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].cmf_lum = 0.; globals::timesteps[n].pellet_decays = 0; } } @@ -2198,4 +2146,4 @@ void write_timestep_file() { globals::timesteps[n].width / DAY); } fclose(timestepfile); -} +} \ No newline at end of file diff --git a/input.h b/input.h index 12d31ed34..bad053cc8 100644 --- a/input.h +++ b/input.h @@ -1,3 +1,4 @@ +#pragma once #ifndef INPUT_H #define INPUT_H @@ -12,14 +13,14 @@ void time_init(); void write_timestep_file(); auto get_noncommentline(std::fstream &input, std::string &line) -> bool; -static inline auto lineiscommentonly(const std::string &line) -> bool +[[nodiscard]] constexpr auto lineiscommentonly(const std::string_view line) -> bool // return true for whitepace-only lines, and lines that are exclusively whitepace up to a '#' character { - for (size_t i = 0; i < line.length(); i++) { - if (line[i] == '#') { // anything to the right of a # character doesn't count + for (char const i : line) { + if (i == '#') { // anything to the right of a # character doesn't count return true; } - if (line[i] != ' ') { + if (i != ' ') { return false; } } diff --git a/kpkt.cc b/kpkt.cc index f396d4d12..73b09717b 100644 --- a/kpkt.cc +++ b/kpkt.cc @@ -1,17 +1,22 @@ #include "kpkt.h" -#include - #include #include +#include +#include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" +#include "globals.h" #include "grid.h" #include "ltepop.h" #include "macroatom.h" +#include "packet.h" #include "radfield.h" #include "ratecoeff.h" #include "rpkt.h" +#include "sn3d.h" #include "stats.h" #include "thermalbalance.h" #include "vectors.h" @@ -19,31 +24,30 @@ namespace kpkt { +namespace { + enum coolingtype { - COOLINGTYPE_FF = 880, - COOLINGTYPE_FB = 881, - COOLINGTYPE_COLLEXC = 882, - COOLINGTYPE_COLLION = 883, + COOLINGTYPE_FF = 0, + COOLINGTYPE_FB = 1, + COOLINGTYPE_COLLEXC = 2, + COOLINGTYPE_COLLION = 3, }; -struct cellhistorycoolinglist { +struct CellCachecoolinglist { enum coolingtype type; - int element; - int ion; int level; int upperlevel; }; -static struct cellhistorycoolinglist *coolinglist; +CellCachecoolinglist *coolinglist; -static auto get_ncoolingterms_ion(int element, int ion) -> int { - return globals::elements[element].ions[ion].ncoolingterms; -} +int n_kpktdiffusion_timesteps{0}; +float kpktdiffusion_timescale{0.}; 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 +auto calculate_cooling_rates_ion(const int modelgridindex, const int element, const int ion, const int indexionstart, + const int cellcacheslotid, 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 { @@ -51,9 +55,7 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem const auto T_e = grid::get_Te(modelgridindex); double C_ion = 0.; - int i = indexionstart; - - const int nions = get_nions(element); + int i = indexionstart; // NOLINT(misc-const-correctness) const int nionisinglevels = get_ionisinglevels(element, ion); const double nncurrention = get_nnion(modelgridindex, element, ion); @@ -66,15 +68,10 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem C_ion += C_ff_ion; if constexpr (update_cooling_contrib_list) { - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + globals::cellcache[cellcacheslotid].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; @@ -103,18 +100,16 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem } } if constexpr (update_cooling_contrib_list) { - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + globals::cellcache[cellcacheslotid].cooling_contrib[i] = C_ion; assert_testmodeonly(coolinglist[i].type == COOLINGTYPE_COLLEXC); - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); i++; } } } - if (ion < nions - 1) { + if (ion < (get_nions(element) - 1)) { const double nnupperion = get_nnion(modelgridindex, element, ion + 1); // ionization to higher ionization stage @@ -132,11 +127,9 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem C_ion += C; if constexpr (update_cooling_contrib_list) { - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + globals::cellcache[cellcacheslotid].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); @@ -160,11 +153,9 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem C_ion += C; if constexpr (update_cooling_contrib_list) { - globals::cellhistory[tid].cooling_contrib[i] = C_ion; + globals::cellcache[cellcacheslotid].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)); @@ -184,7 +175,9 @@ static auto calculate_cooling_rates_ion(const int modelgridindex, const int elem return C_ion; } -void calculate_cooling_rates(const int modelgridindex, struct heatingcoolingrates *heatingcoolingrates) +} // anonymous namespace + +void calculate_cooling_rates(const int modelgridindex, 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 { @@ -195,9 +188,8 @@ void calculate_cooling_rates(const int modelgridindex, struct heatingcoolingrate 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; + grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion] = calculate_cooling_rates_ion( + modelgridindex, element, ion, -1, cellcacheslotid, &C_ff_all, &C_fb_all, &C_exc_all, &C_ionization_all); } } @@ -220,13 +212,20 @@ void calculate_cooling_rates(const int modelgridindex, struct heatingcoolingrate } } +void set_kpktdiffusion(float kpktdiffusion_timescale_in, int n_kpktdiffusion_timesteps_in) { + kpktdiffusion_timescale = kpktdiffusion_timescale_in; + n_kpktdiffusion_timesteps = n_kpktdiffusion_timesteps_in; + printout("input: kpkts diffuse %g of a time step's length for the first %d time steps\n", kpktdiffusion_timescale, + n_kpktdiffusion_timesteps); +} + static void set_ncoolingterms() { - globals::ncoolingterms = 0; + ncoolingterms = 0; for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { int ionterms = 0; - globals::elements[element].ions[ion].coolingoffset = globals::ncoolingterms; + globals::elements[element].ions[ion].coolingoffset = ncoolingterms; /// Ionised ions add one ff-cooling term if (get_ionstage(element, ion) > 1) { @@ -246,7 +245,7 @@ static void set_ncoolingterms() { } } globals::elements[element].ions[ion].ncoolingterms = ionterms; - globals::ncoolingterms += ionterms; + ncoolingterms += ionterms; } } } @@ -261,9 +260,9 @@ void setup_coolinglist() { /// \sum_{elements,ions}get_nlevels(element,ion) and free-free which is \sum_{elements} get_nions(element)-1 set_ncoolingterms(); - const size_t mem_usage_coolinglist = globals::ncoolingterms * sizeof(struct cellhistorycoolinglist); - coolinglist = static_cast( - malloc(globals::ncoolingterms * sizeof(struct cellhistorycoolinglist))); + const size_t mem_usage_coolinglist = ncoolingterms * sizeof(CellCachecoolinglist); + assert_always(ncoolingterms > 0); + coolinglist = static_cast(malloc(ncoolingterms * sizeof(CellCachecoolinglist))); printout("[info] mem_usage: coolinglist occupies %.3f MB\n", mem_usage_coolinglist / 1024. / 1024.); int i = 0; // cooling list index @@ -280,8 +279,6 @@ void setup_coolinglist() { // printout("[debug] ioncharge %d, nncurrention %g, nne %g\n",ion,nncurrention,nne); if (ioncharge > 0) { coolinglist[i].type = COOLINGTYPE_FF; - coolinglist[i].element = element; - coolinglist[i].ion = ion; coolinglist[i].level = -99; coolinglist[i].upperlevel = -99; i++; @@ -290,8 +287,6 @@ void setup_coolinglist() { for (int level = 0; level < nlevels_currention; level++) { if (get_nuptrans(element, ion, level) > 0) { coolinglist[i].type = COOLINGTYPE_COLLEXC; - coolinglist[i].element = element; - coolinglist[i].ion = ion; coolinglist[i].level = level; // upper level is not valid because this is the contribution of all upper levels combined - have to // calculate individually when selecting a random process @@ -307,8 +302,6 @@ void setup_coolinglist() { for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); coolinglist[i].type = COOLINGTYPE_COLLION; - coolinglist[i].element = element; - coolinglist[i].ion = ion; coolinglist[i].level = level; coolinglist[i].upperlevel = upper; i++; @@ -322,8 +315,6 @@ void setup_coolinglist() { for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); coolinglist[i].type = COOLINGTYPE_FB; - coolinglist[i].element = element; - coolinglist[i].ion = ion; coolinglist[i].level = level; coolinglist[i].upperlevel = upper; i++; @@ -334,13 +325,45 @@ void setup_coolinglist() { } } - assert_always(globals::ncoolingterms == i); // if this doesn't match, we miscalculated the number of cooling terms - printout("[info] read_atomicdata: number of coolingterms %d\n", globals::ncoolingterms); + assert_always(ncoolingterms == i); // if this doesn't match, we miscalculated the number of cooling terms + printout("[info] read_atomicdata: number of coolingterms %d\n", ncoolingterms); +} + +static auto sample_planck_analytic(const double T) -> double +// return a randomly chosen frequency according to the Planck distribution of temperature T using an analytic method. +// More testing of this function is needed. +{ + const double nu_peak = 5.879e10 * T; + if (nu_peak > NU_MAX_R || nu_peak < NU_MIN_R) { + printout("[warning] sample_planck: intensity peaks outside frequency range\n"); + } + + constexpr ptrdiff_t nubins = 500; + const auto delta_nu = (NU_MAX_R - NU_MIN_R) / (nubins - 1); + const auto integral_total = radfield::planck_integral_analytic(T, NU_MIN_R, NU_MAX_R, false); + + const double rand_partintegral = rng_uniform() * integral_total; + double prev_partintegral = 0.; + double part_integral = 0.; + double bin_nu_lower = NU_MIN_R; + for (ptrdiff_t i = 1; i < nubins; i++) { + bin_nu_lower = NU_MIN_R + (i - 1) * delta_nu; + const double nu_upper = NU_MIN_R + i * delta_nu; + prev_partintegral = part_integral; + part_integral = radfield::planck_integral_analytic(T, NU_MIN_R, nu_upper, false); + if (rand_partintegral >= part_integral) { + break; + } + } + + // use a linear interpolation for the frequency within the bin + const double nuoffset = (rand_partintegral - prev_partintegral) / (part_integral - prev_partintegral) * delta_nu; + + return bin_nu_lower + nuoffset; } -static auto sample_planck(const double T) -> double -/// returns a randomly chosen frequency according to the Planck -/// distribution of temperature T +static auto sample_planck_montecarlo(const double T) -> double +// return a randomly chosen frequency according to the Planck distribution of temperature T using a Monte Carlo method { const double nu_peak = 5.879e10 * T; if (nu_peak > NU_MAX_R || nu_peak < NU_MIN_R) { @@ -350,364 +373,299 @@ static auto sample_planck(const double T) -> double const double B_peak = radfield::dbb(nu_peak, T, 1); while (true) { - const double zrand = rng_uniform(); - const double zrand2 = rng_uniform(); - const double nu = NU_MIN_R + zrand * (NU_MAX_R - NU_MIN_R); - if (zrand2 * B_peak <= radfield::dbb(nu, T, 1)) { + const double nu = NU_MIN_R + rng_uniform() * (NU_MAX_R - NU_MIN_R); + if (rng_uniform() * B_peak <= radfield::dbb(nu, T, 1)) { return nu; } // printout("[debug] sample_planck: planck_sampling %d\n", i); } } -void do_kpkt_blackbody(struct packet *pkt_ptr) +void do_kpkt_blackbody(Packet &pkt) /// handle a k-packet (e.g., in a thick cell) by emitting according to the planck function { - const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); + const int modelgridindex = grid::get_cell_modelgridindex(pkt.where); - 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 - emit_rpkt(pkt_ptr); + if (EXPANSIONOPACITIES_ON && EXPANSION_OPAC_SAMPLE_KAPPAPLANCK && grid::modelgrid[modelgridindex].thick != 1) { + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + pkt.nu_cmf = sample_planck_times_expansion_opacity(nonemptymgi); + } else { + pkt.nu_cmf = sample_planck_montecarlo(grid::get_Te(modelgridindex)); + // TODO: is this alternative method faster or more accurate or neither? + // pkt.nu_cmf = sample_planck_analytic(grid::get_Te(modelgridindex)); + } + + assert_always(std::isfinite(pkt.nu_cmf)); + /// and then emit the packet randomly in the comoving frame + emit_rpkt(pkt); // 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 + pkt.next_trans = -1; /// 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); - pkt_ptr->interactions++; - 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; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = LASTEVENT_KPKT_TO_RPKT_FFBB; + pkt.emissiontype = EMTYPE_FREEFREE; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; + pkt.nscatterings = 0; } -void do_kpkt(struct packet *pkt_ptr, double t2, int nts) +void do_kpkt(Packet &pkt, 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 modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); + const double t1 = pkt.prop_time; + const int modelgridindex = grid::get_cell_modelgridindex(pkt.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 // printout("[debug] do_kpkt: propagate k-pkt\n"); - const auto T_e = grid::get_Te(modelgridindex); - double deltat = 0.; - if (nts < globals::n_kpktdiffusion_timesteps) { - 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))); + const double deltat = + (nts < n_kpktdiffusion_timesteps) ? kpktdiffusion_timescale * globals::timesteps[nts].width : 0.; + const double t_current = t1 + deltat; 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; + pkt.pos = vec_scale(pkt.pos, t2 / t1); + pkt.prop_time = t2; + return; + } + stats::increment(stats::COUNTER_INTERACTIONS); - /// Randomly select the occuring cooling process - double coolingsum = 0.; - const double zrand = rng_uniform(); + pkt.pos = vec_scale(pkt.pos, t_current / t1); + pkt.prop_time = t_current; - assert_always(grid::modelgrid[modelgridindex].totalcooling > 0.); - const double rndcool_ion = zrand * grid::modelgrid[modelgridindex].totalcooling; + assert_always(grid::modelgrid[modelgridindex].totalcooling > 0.); + const double rndcool_ion = rng_uniform() * grid::modelgrid[modelgridindex].totalcooling; - int element = -1; - int ion = -1; - for (element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (ion = 0; ion < nions; ion++) { - coolingsum += grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]; - // printout("Z=%d, ionstage %d, coolingsum %g\n", get_atomicnumber(element), get_ionstage(element, ion), - // coolingsum); - if (coolingsum > rndcool_ion) { - break; - } - } + /// Randomly select the occuring cooling process + double coolingsum = 0.; + int element = -1; + int ion = -1; + for (element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (ion = 0; ion < nions; ion++) { + coolingsum += grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]; + // printout("Z=%d, ionstage %d, coolingsum %g\n", get_atomicnumber(element), get_ionstage(element, ion), + // coolingsum); if (coolingsum > rndcool_ion) { break; } } - // printout("kpkt selected Z=%d ionstage %d\n", get_atomicnumber(element), get_ionstage(element, ion)); - - if (element >= get_nelements() || element < 0 || ion >= get_nions(element) || ion < 0) { - printout("do_kpkt: problem selecting a cooling process ... abort\n"); - printout("do_kpkt: modelgridindex %d element %d ion %d\n", modelgridindex, element, ion); - printout("do_kpkt: totalcooling %g, coolingsum %g, rndcool_ion %g\n", - grid::modelgrid[modelgridindex].totalcooling, coolingsum, rndcool_ion); - printout("do_kpkt: modelgridindex %d, cellno %d, nne %g\n", modelgridindex, pkt_ptr->where, - grid::get_nne(modelgridindex)); - for (element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (ion = 0; ion < nions; ion++) { - printout("do_kpkt: element %d, ion %d, coolingcontr %g\n", element, ion, - grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]); - } - } - abort(); + if (coolingsum > rndcool_ion) { + break; } + } - // printout("element %d, ion %d, coolingsum %g\n",element,ion,coolingsum); - const int ilow = get_coolinglistoffset(element, ion); - 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); - 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]); + if (element >= get_nelements() || element < 0 || ion >= get_nions(element) || ion < 0) { + printout("do_kpkt: problem selecting a cooling process ... abort\n"); + printout("do_kpkt: modelgridindex %d element %d ion %d\n", modelgridindex, element, ion); + printout("do_kpkt: totalcooling %g, coolingsum %g, rndcool_ion %g\n", grid::modelgrid[modelgridindex].totalcooling, + coolingsum, rndcool_ion); + printout("do_kpkt: modelgridindex %d, cellno %d, nne %g\n", modelgridindex, pkt.where, + grid::get_nne(modelgridindex)); + for (element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (ion = 0; ion < nions; ion++) { + printout("do_kpkt: element %d, ion %d, coolingcontr %g\n", element, ion, + grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]); + } } + std::abort(); + } + + const int ilow = get_coolinglistoffset(element, ion); + const int ihigh = ilow + get_ncoolingterms_ion(element, ion) - 1; + double C_ion_procsum = globals::cellcache[cellcacheslotid].cooling_contrib[ihigh]; + + if (C_ion_procsum < 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); + C_ion_procsum = calculate_cooling_rates_ion(modelgridindex, element, ion, ilow, cellcacheslotid, nullptr, + nullptr, nullptr, nullptr); + assert_testmodeonly( + (C_ion_procsum - grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]) / C_ion_procsum < 1e-3); + } + + // with the ion selected, we now select a level and transition type + + const double rndcool_ion_process = rng_uniform() * C_ion_procsum; + + auto *const selectedvalue = + std::upper_bound(globals::cellcache[cellcacheslotid].cooling_contrib + ilow, + globals::cellcache[cellcacheslotid].cooling_contrib + ihigh + 1, rndcool_ion_process); + const ptrdiff_t i = selectedvalue - globals::cellcache[cellcacheslotid].cooling_contrib; + + if (i > ihigh) { + printout("do_kpkt: error occured while selecting a cooling channel: low %d, high %d, i %td, 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_ion(element, ion), coolingsum); + + printout("lower %g, %g, %g\n", + globals::cellcache[cellcacheslotid].cooling_contrib[get_coolinglistoffset(element, ion) - 1], + globals::cellcache[cellcacheslotid].cooling_contrib[get_coolinglistoffset(element, ion)], + globals::cellcache[cellcacheslotid].cooling_contrib[get_coolinglistoffset(element, ion) + 1]); + const int finalpos = get_coolinglistoffset(element, ion) + get_ncoolingterms_ion(element, ion) - 1; + printout("upper %g, %g, %g\n", globals::cellcache[cellcacheslotid].cooling_contrib[finalpos - 1], + globals::cellcache[cellcacheslotid].cooling_contrib[finalpos], + globals::cellcache[cellcacheslotid].cooling_contrib[finalpos + 1]); + } + + assert_always(i <= ihigh); + + // printout("do_kpkt: selected process %d, coolingsum %g\n", i, coolingsum); + const auto rndcoolingtype = coolinglist[i].type; + const auto T_e = grid::get_Te(modelgridindex); - // with the ion selected, we now select a level and transition type - - const double zrand2 = rng_uniform(); - const double rndcool_ion_process = zrand2 * globals::cellhistory[tid].cooling_contrib[ihigh]; - - auto *const selectedvalue = - std::upper_bound(&globals::cellhistory[tid].cooling_contrib[ilow], - &globals::cellhistory[tid].cooling_contrib[ihigh + 1], rndcool_ion_process); - const ptrdiff_t i = selectedvalue - globals::cellhistory[tid].cooling_contrib; - - if (i > ihigh) { - 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_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]); - 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]); + if (rndcoolingtype == COOLINGTYPE_FF) { + /// The k-packet converts directly into a r-packet by free-free-emission. + /// Need to select the r-packets frequency and a random direction in the + /// co-moving frame. + // printout("[debug] do_kpkt: k-pkt -> free-free\n"); + + /// Sample the packets comoving frame frequency according to paperII 5.4.3 eq.41 + + const double zrand = rng_uniform_pos(); /// delivers zrand in ]0,1[ + pkt.nu_cmf = -KB * T_e / H * log(zrand); + + assert_always(std::isfinite(pkt.nu_cmf)); + + /// and then emit the packet randomly in the comoving frame + emit_rpkt(pkt); + pkt.next_trans = -1; /// FLAG: transition history here not important, cont. process + stats::increment(stats::COUNTER_K_STAT_TO_R_FF); + + pkt.last_event = LASTEVENT_KPKT_TO_RPKT_FFBB; + pkt.emissiontype = EMTYPE_FREEFREE; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; + pkt.nscatterings = 0; + + vpkt_call_estimators(pkt, TYPE_KPKT); + + } else if (rndcoolingtype == 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 + /// co-moving frame. + const int lowerion = ion; + const int lowerlevel = coolinglist[i].level; + const int upper = coolinglist[i].upperlevel; + + /// then randomly sample the packets frequency according to the continuums + /// energy distribution + + // Sample the packets comoving frame frequency according to paperII 4.2.2 + // const double zrand = rng_uniform(); + // if (zrand < 0.5) { + pkt.nu_cmf = select_continuum_nu(element, lowerion, lowerlevel, upper, T_e); + // } else { + // // Emit like a BB + // pkt.nu_cmf = sample_planck(T_e); + // } + + // and then emitt the packet randomly in the comoving frame + emit_rpkt(pkt); + + if constexpr (TRACK_ION_STATS) { + stats::increment_ion_stats(modelgridindex, element, lowerion + 1, stats::ION_RADRECOMB_KPKT, + pkt.e_cmf / H / pkt.nu_cmf); } - assert_always(i <= ihigh); - - // printout("do_kpkt: selected process %d, coolingsum %g\n", i, coolingsum); - - if (coolinglist[i].type == COOLINGTYPE_FF) { - /// The k-packet converts directly into a r-packet by free-free-emission. - /// Need to select the r-packets frequency and a random direction in the - /// co-moving frame. - // printout("[debug] do_kpkt: k-pkt -> free-free\n"); - - /// Sample the packets comoving frame frequency according to paperII 5.4.3 eq.41 - // zrand = rng_uniform(); /// delivers zrand in [0,1[ - // zrand = 1. - zrand; /// make sure that log gets a zrand in ]0,1] - const double zrand = rng_uniform_pos(); /// delivers zrand in ]0,1[ - pkt_ptr->nu_cmf = -KB * T_e / H * log(zrand); - - assert_always(std::isfinite(pkt_ptr->nu_cmf)); - /// and then emitt the packet randomly in the comoving frame - 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->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; - - 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 - /// co-moving frame. - const int element = coolinglist[i].element; - const int lowerion = coolinglist[i].ion; - const int lowerlevel = coolinglist[i].level; - const int upper = coolinglist[i].upperlevel; - - /// then randomly sample the packets frequency according to the continuums - /// energy distribution - - // Sample the packets comoving frame frequency according to paperII 4.2.2 - // 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 - 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); + pkt.next_trans = -1; /// FLAG: transition history here not important, cont. process + stats::increment(stats::COUNTER_K_STAT_TO_R_FB); + pkt.last_event = LASTEVENT_KPKT_TO_RPKT_FB; + pkt.emissiontype = get_emtype_continuum(element, lowerion, lowerlevel, upper); + pkt.trueemissiontype = pkt.emissiontype; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; + pkt.nscatterings = 0; + + vpkt_call_estimators(pkt, TYPE_KPKT); + } else if (rndcoolingtype == COOLINGTYPE_COLLEXC) { + /// the k-packet activates a macro-atom due to collisional excitation + // printout("[debug] do_kpkt: k-pkt -> collisional excitation of MA\n"); + const float nne = grid::get_nne(modelgridindex); + + // if the previous entry belongs to the same ion, then pick up the cumulative sum from + // the previous entry, otherwise start from zero + const double contrib_low = (i > ilow) ? globals::cellcache[cellcacheslotid].cooling_contrib[i - 1] : 0.; + + double contrib = contrib_low; + const int level = coolinglist[i].level; + const double epsilon_current = epsilon(element, ion, level); + const double nnlevel = get_levelpop(modelgridindex, element, ion, level); + const double statweight = stat_weight(element, ion, level); + 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 = 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 * + col_excitation_ratecoeff(T_e, nne, element, ion, level, ii, epsilon_trans, statweight) * + epsilon_trans; + contrib += C; + if (contrib > rndcool_ion_process) { + upper = tmpupper; + break; } + } - pkt_ptr->next_trans = 0; /// FLAG: transition history here not important, cont. process - stats::increment(stats::COUNTER_K_STAT_TO_R_FB); - pkt_ptr->last_event = 7; - pkt_ptr->emissiontype = get_continuumindex(element, lowerion, lowerlevel, upper); - pkt_ptr->trueemissiontype = pkt_ptr->emissiontype; - vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); - pkt_ptr->em_time = pkt_ptr->prop_time; - pkt_ptr->nscatterings = 0; - - 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"); - const float nne = grid::get_nne(modelgridindex); - - // if the previous entry belongs to the same ion, then pick up the cumulative sum from - // the previous entry, otherwise start from zero - const double contrib_low = (i > ilow) ? globals::cellhistory[tid].cooling_contrib[i - 1] : 0.; - - double contrib = contrib_low; - assert_testmodeonly(coolinglist[i].element == element); - assert_testmodeonly(coolinglist[i].ion == ion); - const int level = coolinglist[i].level; - const double epsilon_current = epsilon(element, ion, level); - const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - const double statweight = stat_weight(element, ion, level); - 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 = 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 * - col_excitation_ratecoeff(T_e, nne, element, ion, level, ii, epsilon_trans, statweight) * - epsilon_trans; - contrib += C; - if (contrib > rndcool_ion_process) { - upper = tmpupper; - break; - } - } + if (upper < 0) { + printout( + "WARNING: Could not select an upper level. modelgridindex %d i %td element %d ion %d level %d rndcool " + "%g " + "contrib_low %g contrib %g (should match %g) upper %d nuptrans %d\n", + modelgridindex, i, element, ion, level, rndcool_ion_process, contrib_low, contrib, + globals::cellcache[cellcacheslotid].cooling_contrib[i], upper, nuptrans); + std::abort(); + } + assert_always(upper >= 0); - if (upper < 0) { - printout( - "WARNING: Could not select an upper level. modelgridindex %d i %d element %d ion %d level %d rndcool " - "%g " - "contrib_low %g contrib %g (should match %g) upper %d nuptrans %d\n", - modelgridindex, i, element, ion, level, rndcool_ion_process, contrib_low, contrib, - globals::cellhistory[tid].cooling_contrib[i], upper, nuptrans); - abort(); - } - assert_always(upper >= 0); - - const int element = coolinglist[i].element; - const int ion = coolinglist[i].ion; - // const int upper = coolinglist[i].upperlevel; - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion; - pkt_ptr->mastate.level = upper; - pkt_ptr->mastate.activatingline = -99; - - if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLEXC, pkt_ptr->e_cmf); - } + if constexpr (TRACK_ION_STATS) { + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLEXC, pkt.e_cmf); + } - pkt_ptr->type = TYPE_MA; - stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLEXC); - stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLEXC); - - pkt_ptr->last_event = 8; - pkt_ptr->trueemissiontype = EMTYPE_NOTSET; - pkt_ptr->trueemissionvelocity = -1; - } else if (coolinglist[i].type == COOLINGTYPE_COLLION) { - /// the k-packet activates a macro-atom due to collisional ionisation - // printout("[debug] do_kpkt: k-pkt -> collisional ionisation of MA\n"); - const int element = coolinglist[i].element; - const int ion = coolinglist[i].ion + 1; - const int upper = coolinglist[i].upperlevel; - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion; - pkt_ptr->mastate.level = upper; - pkt_ptr->mastate.activatingline = -99; - - if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLION, pkt_ptr->e_cmf); - } + pkt.type = TYPE_MA; + stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLEXC); + stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLEXC); - pkt_ptr->type = TYPE_MA; - stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLION); - stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLION); + pkt.last_event = 8; + pkt.trueemissiontype = EMTYPE_NOTSET; + pkt.trueemissionvelocity = -1; - pkt_ptr->last_event = 9; - pkt_ptr->trueemissiontype = EMTYPE_NOTSET; - pkt_ptr->trueemissionvelocity = -1; - } else { - printout("[fatal] do_kpkt: coolinglist.type mismatch\n"); - printout("[fatal] do_kpkt: zrand %g, grid::modelgrid[modelgridindex].totalcooling %g, coolingsum %g, i %d\n", - zrand, grid::modelgrid[modelgridindex].totalcooling, coolingsum, i); - printout("[fatal] do_kpkt: coolinglist[i].type %d\n", coolinglist[i].type); - printout("[fatal] do_kpkt: pkt_ptr->where %d, mgi %d\n", pkt_ptr->where, modelgridindex); - abort(); + do_macroatom(pkt, {element, ion, upper, -99}); + } else if (rndcoolingtype == COOLINGTYPE_COLLION) { + /// the k-packet activates a macro-atom due to collisional ionisation + // printout("[debug] do_kpkt: k-pkt -> collisional ionisation of MA\n"); + + const int upperion = ion + 1; + const int upper = coolinglist[i].upperlevel; + + if constexpr (TRACK_ION_STATS) { + stats::increment_ion_stats(modelgridindex, element, upperion, stats::ION_MACROATOM_ENERGYIN_COLLION, pkt.e_cmf); } - pkt_ptr->interactions++; + pkt.type = TYPE_MA; + stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_COLLION); + stats::increment(stats::COUNTER_K_STAT_TO_MA_COLLION); + + pkt.last_event = 9; + pkt.trueemissiontype = EMTYPE_NOTSET; + pkt.trueemissionvelocity = -1; + + do_macroatom(pkt, {element, upperion, upper, -99}); + } else if constexpr (TESTMODE) { + printout("ERROR: Unknown rndcoolingtype type %d\n", rndcoolingtype); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); } } -/*static int compare_coolinglistentry(const void *p1, const void *p2) -/// Helper function to sort the coolinglist by the strength of the -/// individual cooling contributions. -{ - ionscoolinglist_t *a1, *a2; - a1 = (ionscoolinglist_t *)(p1); - a2 = (ionscoolinglist_t *)(p2); - //printf("%d %d %d %d %g\n",a1->elementindex,a1->ionindex,a1->lowerlevelindex,a1->upperlevelindex,a1->nu); - //printf("%d %d %d %d %g\n",a2->elementindex,a2->ionindex,a2->lowerlevelindex,a2->upperlevelindex,a2->nu); - //printf("%g\n",a2->nu - a1->nu); - if (a1->contribution - a2->contribution < 0) - return 1; - else if (a1->contribution - a2->contribution > 0) - return -1; - else - return 0; -}*/ - -/*double get_bfcooling_direct(int element, int ion, int level, int cellnumber) -/// Returns the rate for bfheating. This can be called during packet propagation -/// or update_grid. Therefore we need to decide whether a cell history is -/// known or not. -{ - double bfcooling; - - gsl_integration_workspace *wsp; - gslintegration_paras intparas; - double bfcooling_integrand_gsl(double nu, void *paras); - gsl_function F_bfcooling; - F_bfcooling.function = &bfcooling_integrand_gsl; - double intaccuracy = 1e-2; /// Fractional accuracy of the integrator - double error; - double nu_max_phixs; - - float T_e = globals::cell[cellnumber].T_e; - float nne = globals::cell[cellnumber].nne; - double nnionlevel = get_groundlevelpop(cellnumber,element,ion+1); - //upper = coolinglist[i].upperlevel; - double nu_threshold = (epsilon(element,ion+1,0) - epsilon(element,ion,level)) / H; - nu_max_phixs = nu_threshold * last_phixs_nuovernuedge; //nu of the uppermost point in the phixs table - - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion; - pkt_ptr->mastate.level = level; - intparas.T = T_e; - intparas.nu_edge = nu_threshold; /// Global variable which passes the threshold to the integrator - F_bfcooling.params = &intparas; - gsl_integration_qag(&F_bfcooling, nu_threshold, nu_max_phixs, 0, intaccuracy, 1024, 6, wsp, &bfcooling, &error); - bfcooling *= nnionlevel*nne*4*PI*calculate_sahafact(element,ion,level,upperionlevel,T_e,nu_threshold*H); - - return bfcooling; -}*/ - } // namespace kpkt \ No newline at end of file diff --git a/kpkt.h b/kpkt.h index 28bb64b11..60bbdc588 100644 --- a/kpkt.h +++ b/kpkt.h @@ -1,3 +1,4 @@ +#pragma once #ifndef KPKT_H #define KPKT_H @@ -9,15 +10,23 @@ constexpr double COOLING_UNDEFINED = -99; namespace kpkt { +inline int ncoolingterms{0}; + +auto get_ncoolingterms() -> int; void setup_coolinglist(); -void calculate_cooling_rates(int modelgridindex, struct heatingcoolingrates *heatingcoolingrates); -void do_kpkt_blackbody(struct packet *pkt_ptr); -void do_kpkt(struct packet *pkt_ptr, double t2, int nts); +void set_kpktdiffusion(float kpktdiffusion_timescale_in, int n_kpktdiffusion_timesteps_in); +void calculate_cooling_rates(int modelgridindex, HeatingCoolingRates *heatingcoolingrates); +void do_kpkt_blackbody(Packet &pkt); +void do_kpkt(Packet &pkt, double t2, int nts); -static inline auto get_coolinglistoffset(int element, int ion) -> int { +[[nodiscard]] inline auto get_coolinglistoffset(int element, int ion) -> int { return globals::elements[element].ions[ion].coolingoffset; } +[[nodiscard]] inline auto get_ncoolingterms_ion(int element, int ion) -> int { + return globals::elements[element].ions[ion].ncoolingterms; +} + } // namespace kpkt #endif // KPKT_H diff --git a/light_curve.cc b/light_curve.cc deleted file mode 100644 index bb2bdb1ee..000000000 --- a/light_curve.cc +++ /dev/null @@ -1,73 +0,0 @@ -#include "light_curve.h" - -#include "exspec.h" -#include "sn3d.h" -#include "vectors.h" - -// 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 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) { - throw std::system_error(errno, std::system_category(), "failed to open " + lc_filename); - } - - printout("Writing %s\n", lc_filename.c_str()); - - constexpr int maxlen = 1024; - char linebuffer[maxlen]; - - /// Print out the UVOIR bolometric light curve. - for (int nts = 0; nts < numtimesteps; nts++) { - 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'; - } - - 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::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, std::vector &light_curve_lum, - std::vector &light_curve_lumcmf) -// add a packet to the outgoing light-curve. -{ - if (current_abin == -1) { - /// Put this into the time grid - 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::timesteps[nt].width / globals::nprocs_exspec); - } - - /// Now do the cmf light curve. - // t_arrive = pkt_ptr->escape_time * sqrt(1. - (vmax*vmax/CLIGHTSQUARED)); - 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::timesteps[nt].width / globals::nprocs_exspec / - sqrt(1. - (globals::vmax * globals::vmax / CLIGHTSQUARED))); - } - - return; - } - if (get_escapedirectionbin(pkt_ptr->dir, globals::syn_dir) == current_abin) { - // Add only packets which escape to the current angle bin - const double t_arrive = get_arrive_time(pkt_ptr); - if (t_arrive > globals::tmin && t_arrive < globals::tmax) { - 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 deleted file mode 100644 index b2cc4d5bc..000000000 --- a/light_curve.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef LIGHT_CURVE_H -#define LIGHT_CURVE_H - -#include -#include - -#include "exspec.h" - -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 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 e811e5550..e81ae5347 100644 --- a/ltepop.cc +++ b/ltepop.cc @@ -1,12 +1,19 @@ #include "ltepop.h" #include +#include #include +#include #include +#include +#include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "nltepop.h" #include "nonthermal.h" @@ -14,12 +21,14 @@ #include "rpkt.h" #include "sn3d.h" +namespace { + struct nne_solution_paras { int modelgridindex; bool force_lte; }; -static auto interpolate_ions_spontrecombcoeff(const int element, const int ion, const double T) -> double { +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); @@ -37,7 +46,7 @@ static auto interpolate_ions_spontrecombcoeff(const int element, const int ion, return globals::elements[element].ions[ion].Alpha_sp[TABLESIZE - 1]; } -static auto phi_lte(const int element, const int ion, const int modelgridindex) -> double { +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]; @@ -48,7 +57,7 @@ static auto phi_lte(const int element, const int ion, const int modelgridindex) 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 +auto phi_ion_equilib(const int element, const int ion, const int modelgridindex, const int nonemptymgi) -> 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) { @@ -68,7 +77,7 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri double Gamma = 0.; if constexpr (USE_LUT_PHOTOION) { - Gamma = globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)]; + Gamma = globals::gammaestimator[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)]; } else { Gamma = calculate_iongamma_per_gspop(modelgridindex, element, ion); } @@ -76,11 +85,11 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri // Gamma is the photoionization rate per ground level pop const double Gamma_ion = Gamma * stat_weight(element, ion, 0) / partfunc_ion; - if (Gamma == 0. && (!NT_ON || (globals::rpkt_emiss[modelgridindex] == 0. && + if (Gamma == 0. && (!NT_ON || (globals::dep_estimator_gamma[nonemptymgi] == 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(); + std::abort(); } const double Alpha_sp = interpolate_ions_spontrecombcoeff(element, ion, T_e); @@ -106,12 +115,14 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri 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(); + std::abort(); } return phi; } +} // anonymous namespace + [[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 @@ -121,6 +132,7 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri assert_testmodeonly(modelgridindex < grid::get_npts_model()); assert_testmodeonly(element < get_nelements()); assert_testmodeonly(uppermost_ion <= std::max(0, get_nions(element) - 1)); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); if (uppermost_ion < 0) { return {}; @@ -132,8 +144,8 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri double normfactor = 1.; 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); + const auto phifactor = use_phi_lte ? phi_lte(element, ion, modelgridindex) + : phi_ion_equilib(element, ion, modelgridindex, nonemptymgi); ionfractions[ion] = ionfractions[ion + 1] * nne * phifactor; normfactor += ionfractions[ion]; } @@ -141,7 +153,7 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri for (int ion = 0; ion <= uppermost_ion; ion++) { ionfractions[ion] = ionfractions[ion] / normfactor; - if (!std::isfinite(ionfractions[ion]) && modelgridindex != grid::get_npts_model()) { + if (normfactor == 0. || !std::isfinite(ionfractions[ion])) { 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)); @@ -153,26 +165,25 @@ static auto phi_ion_equilib(const int element, const int ion, const int modelgri 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; + if (grid::get_elem_numberdens(modelgridindex, element) <= 0.) { + return 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 0.; + return nne; } 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 { - const auto *paras = reinterpret_cast(voidparas); + const auto *paras = static_cast(voidparas); const int modelgridindex = paras->modelgridindex; const bool force_lte = paras->force_lte; @@ -248,12 +259,13 @@ static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion assert_testmodeonly(ion < get_nions(element)); assert_testmodeonly(level < get_nlevels(element, ion)); - double nn = NAN; + double nn{NAN}; if (level == 0) { nn = get_groundlevelpop(modelgridindex, element, ion); } else if (elem_has_nlte_levels(element)) { if (is_nlte(element, ion, level)) { + // first_nlte refers to the first excited state (level=1) 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) { @@ -267,7 +279,7 @@ static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion printout("element %d ion %d level %d\n", element, ion, level); printout("nn %g nltepop_over_rho %g rho %g\n", nn, nltepop_over_rho, grid::get_rho(modelgridindex)); printout("ground level %g\n", get_groundlevelpop(modelgridindex, element, ion)); - abort(); + std::abort(); } *skipminpop = true; return nn; @@ -293,7 +305,7 @@ static auto calculate_levelpop_nominpop(int modelgridindex, int element, int ion printout("nn %g superlevelpop_over_rho %g rho %g\n", nn, superlevelpop_over_rho, grid::get_rho(modelgridindex)); printout("ground level %g\n", get_groundlevelpop(modelgridindex, element, ion)); - abort(); + std::abort(); } *skipminpop = true; return nn; @@ -324,9 +336,9 @@ auto get_levelpop(int modelgridindex, int element, int ion, int level) -> double /// Calculates the population of a level from either LTE or NLTE information { double nn = 0.; - if (use_cellhist) { - assert_testmodeonly(modelgridindex == globals::cellhistory[tid].cellnumber); - nn = globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].population; + if (use_cellcache) { + assert_testmodeonly(modelgridindex == globals::cellcache[cellcacheslotid].cellnumber); + nn = globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level].population; } else { nn = calculate_levelpop(modelgridindex, element, ion, level); } @@ -344,7 +356,7 @@ static auto calculate_partfunct(int element, int ion, int modelgridindex) -> dou assert_testmodeonly(modelgridindex < grid::get_npts_model()); assert_testmodeonly(element < get_nelements()); assert_testmodeonly(ion < get_nions(element)); - double pop_store = NAN; + double pop_store{NAN}; // double E_level, E_ground, test; bool initial = false; @@ -354,7 +366,7 @@ static auto calculate_partfunct(int element, int ion, int modelgridindex) -> dou // of groundlevelpop for this calculation doesn't matter, so long as it's not zero! pop_store = get_groundlevelpop(modelgridindex, element, ion); initial = true; - grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = 1.0; + grid::modelgrid[modelgridindex].composition[element].groundlevelpop[ion] = 1.; } double U = 1.; @@ -373,7 +385,7 @@ static auto calculate_partfunct(int element, int ion, int modelgridindex) -> dou printout("modelgridindex %d\n", modelgridindex); printout("nlevels %d\n", nlevels); printout("sw %g\n", stat_weight(element, ion, 0)); - abort(); + std::abort(); } if (initial) { @@ -412,7 +424,7 @@ auto calculate_sahafact(int element, int ion, int level, int upperionlevel, doub "[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(); + std::abort(); } return sf; } @@ -433,30 +445,29 @@ static auto find_uppermost_ion(const int modelgridindex, const int element, cons if (!force_lte && elem_has_nlte_levels(element)) { return nions - 1; } + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); 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.) && + uppermost_ion = nions - 1; + if (!use_lte) { + for (int ion = 0; ion < nions - 1; ion++) { + if (iongamma_is_zero(nonemptymgi, element, ion) && + (!NT_ON || ((globals::dep_estimator_gamma[nonemptymgi] == 0.) && (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(24, 48)) == 0.) && (grid::get_modelinitradioabund(modelgridindex, decay::get_nucindex(28, 56)) == 0.)))) { + uppermost_ion = ion; 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); + use_lte ? phi_lte(element, ion, modelgridindex) : phi_ion_equilib(element, ion, modelgridindex, nonemptymgi); factor *= nne_hi * phifactor; if (!std::isfinite(factor)) { @@ -493,14 +504,16 @@ void set_groundlevelpops(const int modelgridindex, const int element, const floa /// calculate number density of the current element (abundances are given by mass) const double nnelement = grid::get_elem_numberdens(modelgridindex, element); + const bool use_phi_lte = force_lte || FORCE_SAHA_ION_BALANCE(get_atomicnumber(element)); + const auto ionfractions = - (nnelement > 0) ? calculate_ionfractions(element, modelgridindex, nne, force_lte) : std::vector(); + (nnelement > 0) ? calculate_ionfractions(element, modelgridindex, nne, use_phi_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; + double nnion{NAN}; if (ion <= uppermost_ion) { if (nnelement > 0) { nnion = std::max(MINPOP, nnelement * ionfractions[ion]); @@ -531,7 +544,7 @@ static void set_groundlevelpops_neutral(const int modelgridindex) { 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; + double nnion{NAN}; if (ion == 0) { nnion = nnelement; } else if (nnelement > 0.) { @@ -550,7 +563,7 @@ static void set_groundlevelpops_neutral(const int modelgridindex) { 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}; + nne_solution_paras paras = {.modelgridindex = modelgridindex, .force_lte = force_lte}; gsl_function f = {.function = &nne_solution_f, .params = ¶s}; double nne_lo = 0.; // MINPOP; @@ -570,7 +583,8 @@ static auto find_converged_nne(const int modelgridindex, double nne_hi, const bo 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)]); + globals::gammaestimator[get_ionestimindex_nonemptymgi( + grid::get_modelcell_nonemptymgi(modelgridindex), element, ion)]); } } } diff --git a/ltepop.h b/ltepop.h index d48f165d3..036605101 100644 --- a/ltepop.h +++ b/ltepop.h @@ -1,16 +1,13 @@ +#pragma once #ifndef LTEPOP_H #define LTEPOP_H -#include #include -#include "atomic.h" -#include "sn3d.h" - -auto get_groundlevelpop(int modelgridindex, int element, int ion) -> double; -auto calculate_levelpop(int modelgridindex, int element, int ion, int level) -> double; -auto calculate_levelpop_lte(int modelgridindex, int element, int ion, int level) -> double; -auto get_levelpop(int modelgridindex, int element, int ion, int level) -> double; +[[nodiscard]] auto get_groundlevelpop(int modelgridindex, int element, int ion) -> double; +[[nodiscard]] auto calculate_levelpop(int modelgridindex, int element, int ion, int level) -> double; +[[nodiscard]] auto calculate_levelpop_lte(int modelgridindex, int element, int ion, int level) -> double; +[[nodiscard]] auto get_levelpop(int modelgridindex, int element, int ion, int level) -> double; [[nodiscard]] auto calculate_sahafact(int element, int ion, int level, int upperionlevel, double T, double E_threshold) -> double; [[nodiscard]] auto get_nnion(int modelgridindex, int element, int ion) -> double; diff --git a/macroatom.cc b/macroatom.cc index 7cc647cc0..91e06a13d 100644 --- a/macroatom.cc +++ b/macroatom.cc @@ -1,15 +1,23 @@ #include "macroatom.h" -#include - #include +#include #include +#include +#include +#include +#include +#include +#include #include "artisoptions.h" +#include "atomic.h" +#include "constants.h" #include "globals.h" #include "grid.h" #include "ltepop.h" #include "nonthermal.h" +#include "packet.h" #include "radfield.h" #include "ratecoeff.h" #include "rpkt.h" @@ -18,13 +26,15 @@ #include "vectors.h" #include "vpkt.h" +namespace { + // save to the macroatom_*.out file constexpr bool LOG_MACROATOM = false; -static FILE *macroatom_file = nullptr; +FILE *macroatom_file{}; -static void calculate_macroatom_transitionrates(const int modelgridindex, const int element, const int ion, - const int level, const double t_mid, struct chlevels &chlevel) { +void calculate_macroatom_transitionrates(const int modelgridindex, const int element, const int ion, const int level, + const double t_mid, CellCacheLevels &chlevel) { // printout("Calculating transition rates for element %d ion %d level %d\n", element, ion, level); auto &processrates = chlevel.processrates; const auto T_e = grid::get_Te(modelgridindex); @@ -36,9 +46,9 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const /// Downward transitions within the current ionisation stage: /// radiative/collisional deexcitation and internal downward jumps - processrates[MA_ACTION_RADDEEXC] = 0.; - processrates[MA_ACTION_COLDEEXC] = 0.; - processrates[MA_ACTION_INTERNALDOWNSAME] = 0.; + double sum_internal_down_same = 0.; + double sum_raddeexc = 0.; + double sum_coldeexc = 0.; const int ndowntrans = get_ndowntrans(element, ion, level); for (int i = 0; i < ndowntrans; i++) { const auto &downtransition = levelref.downtrans[i]; @@ -56,22 +66,25 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const const double sum_epstrans_rad_deexc = R * epsilon_trans; const double individ_col_deexc = C * epsilon_trans; - processrates[MA_ACTION_RADDEEXC] += sum_epstrans_rad_deexc; - processrates[MA_ACTION_COLDEEXC] += individ_col_deexc; - processrates[MA_ACTION_INTERNALDOWNSAME] += individ_internal_down_same; + sum_raddeexc += sum_epstrans_rad_deexc; + sum_coldeexc += individ_col_deexc; + sum_internal_down_same += 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] = sum_raddeexc; + chlevel.sum_internal_down_same[i] = sum_internal_down_same; // printout("checking downtrans %d to level %d: R %g, C %g, epsilon_trans %g\n",i,lower,R,C,epsilon_trans); } + processrates[MA_ACTION_RADDEEXC] = sum_raddeexc; + processrates[MA_ACTION_COLDEEXC] = sum_coldeexc; + processrates[MA_ACTION_INTERNALDOWNSAME] = sum_internal_down_same; /// Downward transitions to lower ionisation stages: /// radiative/collisional recombination and internal downward jumps - processrates[MA_ACTION_RADRECOMB] = 0.; - processrates[MA_ACTION_COLRECOMB] = 0.; - processrates[MA_ACTION_INTERNALDOWNLOWER] = 0.; // checks only if there is a lower ion, doesn't make sure that Z(ion)=Z(ion-1)+1 + double sum_internal_down_lower = 0.; + double sum_radrecomb = 0.; + double sum_colrecomb = 0.; if (ion > 0 && level <= get_maxrecombininglevel(element, ion)) { // nlevels = get_nlevels(element,ion-1); const int nlevels = get_ionisinglevels(element, ion - 1); @@ -83,16 +96,19 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const const double R = rad_recombination_ratecoeff(T_e, nne, element, ion, level, lower, modelgridindex); const double C = col_recombination_ratecoeff(modelgridindex, element, ion, level, lower, epsilon_trans); - processrates[MA_ACTION_INTERNALDOWNLOWER] += (R + C) * epsilon_target; + sum_internal_down_lower += (R + C) * epsilon_target; - processrates[MA_ACTION_RADRECOMB] += R * epsilon_trans; - processrates[MA_ACTION_COLRECOMB] += C * epsilon_trans; + sum_radrecomb += R * epsilon_trans; + sum_colrecomb += C * epsilon_trans; } } + processrates[MA_ACTION_INTERNALDOWNLOWER] = sum_internal_down_lower; + processrates[MA_ACTION_RADRECOMB] = sum_radrecomb; + processrates[MA_ACTION_COLRECOMB] = sum_colrecomb; /// Calculate sum for upward internal transitions /// transitions within the current ionisation stage - processrates[MA_ACTION_INTERNALUPSAME] = 0.; + double sum_internal_up_same = 0.; const int nuptrans = get_nuptrans(element, ion, level); for (int i = 0; i < nuptrans; i++) { const auto &uptransition = globals::elements[element].ions[ion].levels[level].uptrans[i]; @@ -107,23 +123,24 @@ 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]; + sum_internal_up_same += individ_internal_up_same; + chlevel.sum_internal_up_same[i] = sum_internal_up_same; } + processrates[MA_ACTION_INTERNALUPSAME] = sum_internal_up_same; assert_always(std::isfinite(processrates[MA_ACTION_INTERNALUPSAME])); /// Transitions to higher ionisation stages - processrates[MA_ACTION_INTERNALUPHIGHERNT] = 0.; - processrates[MA_ACTION_INTERNALUPHIGHER] = 0.; + double sum_up_highernt = 0.; + double sum_up_higher = 0.; const int ionisinglevels = get_ionisinglevels(element, ion); if (ion < get_nions(element) - 1 && level < ionisinglevels) { if (NT_ON) { - processrates[MA_ACTION_INTERNALUPHIGHERNT] = - nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion) * epsilon_current; + sum_up_highernt = nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion) * epsilon_current; } - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { + const auto 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_trans = epsilon(element, ion + 1, upper) - epsilon(element, ion, level); const double epsilon_trans = get_phixs_threshold(element, ion, level, phixstargetindex); @@ -131,22 +148,23 @@ static void calculate_macroatom_transitionrates(const int modelgridindex, const const double R = get_corrphotoioncoeff(element, ion, level, phixstargetindex, modelgridindex); const double C = col_ionization_ratecoeff(T_e, nne, element, ion, level, phixstargetindex, epsilon_trans); - processrates[MA_ACTION_INTERNALUPHIGHER] += (R + C) * epsilon_current; + sum_up_higher += (R + C) * epsilon_current; } } + processrates[MA_ACTION_INTERNALUPHIGHERNT] = sum_up_highernt; + processrates[MA_ACTION_INTERNALUPHIGHER] = sum_up_higher; } -static auto do_macroatom_internal_down_same(int element, int ion, int level, double total_internal_down_same) -> int { +auto do_macroatom_internal_down_same(int element, int ion, int level) -> int { const int ndowntrans = get_ndowntrans(element, ion, level); // printout("[debug] do_ma: internal downward jump within current ionstage\n"); - /// Randomly select the occuring transition - const double zrand = rng_uniform(); - const double targetval = zrand * total_internal_down_same; - const double *sum_internal_down_same = - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].sum_internal_down_same; + globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level].sum_internal_down_same; + + /// Randomly select the occuring transition + const double targetval = rng_uniform() * sum_internal_down_same[ndowntrans - 1]; // first sum_internal_down_same[i] such that sum_internal_down_same[i] > targetval const double *const upperval = @@ -159,80 +177,66 @@ static auto do_macroatom_internal_down_same(int element, int ion, int level, dou return lower; } -static void do_macroatom_raddeexcitation(struct packet *pkt_ptr, const int element, const int ion, const int level, - const double rad_deexc, const int activatingline) { +void do_macroatom_raddeexcitation(Packet &pkt, const int element, const int ion, const int level, + const int activatingline) { /// radiative deexcitation of MA: emitt rpkt /// randomly select which line transitions occurs const int ndowntrans = get_ndowntrans(element, ion, level); - double *sum_epstrans_rad_deexc = - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].sum_epstrans_rad_deexc; + const auto *sum_epstrans_rad_deexc = + globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level].sum_epstrans_rad_deexc; - const double zrand = rng_uniform(); - const double targetval = zrand * rad_deexc; + const double targetval = rng_uniform() * sum_epstrans_rad_deexc[ndowntrans - 1]; // first sum_epstrans_rad_deexc[i] such that sum_epstrans_rad_deexc[i] > targetval - const double *const upperval = - std::upper_bound(&sum_epstrans_rad_deexc[0], &sum_epstrans_rad_deexc[ndowntrans], targetval); - const ptrdiff_t downtransindex = upperval - &sum_epstrans_rad_deexc[0]; + const auto downtransindex = std::distance( + sum_epstrans_rad_deexc, std::upper_bound(sum_epstrans_rad_deexc, sum_epstrans_rad_deexc + ndowntrans, targetval)); assert_always(downtransindex < ndowntrans); - auto linelistindex = globals::elements[element].ions[ion].levels[level].downtrans[downtransindex].lineindex; - if (linelistindex == activatingline) { + const auto &selecteddowntrans = globals::elements[element].ions[ion].levels[level].downtrans[downtransindex]; + + if (selecteddowntrans.lineindex == activatingline) { stats::increment(stats::COUNTER_RESONANCESCATTERINGS); } if constexpr (RECORD_LINESTAT) { - safeincrement(globals::ecounter[linelistindex]); + atomicadd(globals::ecounter[selecteddowntrans.lineindex], 1); } - const int lower = globals::elements[element].ions[ion].levels[level].downtrans[downtransindex].targetlevelindex; - - // printout("[debug] do_ma: jump to level %d\n", lower); + const double epsilon_trans = epsilon(element, ion, level) - epsilon(element, ion, selecteddowntrans.targetlevelindex); - const double epsilon_trans = epsilon(element, ion, level) - epsilon(element, ion, lower); + const double oldnucmf{(pkt.last_event == 1) ? pkt.nu_cmf : NAN}; + pkt.nu_cmf = epsilon_trans / H; - double oldnucmf = NAN; - if (pkt_ptr->last_event == 1) { - oldnucmf = pkt_ptr->nu_cmf; - } - pkt_ptr->nu_cmf = epsilon_trans / H; - - if (pkt_ptr->last_event == 1) { - if (oldnucmf < pkt_ptr->nu_cmf) { - stats::increment(stats::COUNTER_UPSCATTER); - } else { - stats::increment(stats::COUNTER_DOWNSCATTER); - } + if (pkt.last_event == 1) { + stats::increment((oldnucmf < pkt.nu_cmf) ? stats::COUNTER_UPSCATTER : stats::COUNTER_DOWNSCATTER); } stats::increment(stats::COUNTER_MA_STAT_DEACTIVATION_BB); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 0; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 0; // emit the rpkt in a random direction - emit_rpkt(pkt_ptr); + emit_rpkt(pkt); // the r-pkt can only interact with lines redder than the current one - pkt_ptr->next_trans = linelistindex + 1; - pkt_ptr->emissiontype = linelistindex; - vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); - pkt_ptr->em_time = pkt_ptr->prop_time; - pkt_ptr->nscatterings = 0; - - vpkt_call_estimators(pkt_ptr, TYPE_MA); + pkt.next_trans = selecteddowntrans.lineindex + 1; + pkt.emissiontype = selecteddowntrans.lineindex; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; + pkt.nscatterings = 0; } -static void do_macroatom_radrecomb(struct packet *pkt_ptr, const int modelgridindex, const int element, int *ion, - int *level, const double rad_recomb) { +void do_macroatom_radrecomb(Packet &pkt, const int modelgridindex, const int element, int *ion, int *level, + const double rad_recomb) { const auto T_e = grid::get_Te(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 - const double zrand = rng_uniform(); + const double targetval = rng_uniform() * rad_recomb; double rate = 0; const int nlevels = get_ionisinglevels(element, upperion - 1); int lower = 0; @@ -242,106 +246,84 @@ static void do_macroatom_radrecomb(struct packet *pkt_ptr, const int modelgridin rate += R * epsilon_trans; - // printout("[debug] do_ma: R %g, deltae %g\n",R,(epsilon(element, upperion, upperionlevel) - epsilon(element, - // upperion - 1, lower))); printout("[debug] do_ma: rate to level %d of ion %d = %g\n", lower, upperion - 1, - // rate); printout("[debug] do_ma: zrand*rad_recomb = %g\n", zrand * rad_recomb); - - if (zrand * rad_recomb < rate) { + if (targetval < rate) { break; } } - if (zrand * rad_recomb >= rate) { + if (targetval >= rate) { printout( - "%s: From Z=%d ionstage %d level %d, could not select lower level to recombine to. zrand %g * rad_recomb %g >= " + "%s: From Z=%d ionstage %d level %d, could not select lower level to recombine to. targetval %g * rad_recomb " + "%g >= " "rate %g", - __func__, get_atomicnumber(element), get_ionstage(element, *ion), *level, zrand, rad_recomb, rate); - abort(); + __func__, get_atomicnumber(element), get_ionstage(element, *ion), *level, targetval, rad_recomb, rate); + std::abort(); } /// set the new state *ion = upperion - 1; *level = lower; - pkt_ptr->nu_cmf = select_continuum_nu(element, upperion - 1, lower, upperionlevel, T_e); - - // printout("%s: From Z=%d ionstage %d, recombining to ionstage %d level %d\n", - // __func__, get_atomicnumber(element), get_ionstage(element, *ion + 1), get_ionstage(element, *ion), lower); - // printout("[debug] do_ma: pkt_ptr->nu_cmf %g\n",pkt_ptr->nu_cmf); + pkt.nu_cmf = select_continuum_nu(element, upperion - 1, lower, upperionlevel, T_e); - if (!std::isfinite(pkt_ptr->nu_cmf)) { + if (!std::isfinite(pkt.nu_cmf)) { printout("[fatal] rad recombination of MA: selected frequency not finite ... abort\n"); - abort(); + std::abort(); } stats::increment(stats::COUNTER_MA_STAT_DEACTIVATION_FB); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 2; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 2; /// Finally emit the packet into a randomly chosen direction, update the continuum opacity and set some flags - emit_rpkt(pkt_ptr); + emit_rpkt(pkt); 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); + pkt.e_cmf / H / pkt.nu_cmf); } - pkt_ptr->next_trans = 0; /// continuum transition, no restrictions for further line interactions - pkt_ptr->emissiontype = get_continuumindex(element, *ion, lower, upperionlevel); - vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); - pkt_ptr->em_time = pkt_ptr->prop_time; - pkt_ptr->nscatterings = 0; - - vpkt_call_estimators(pkt_ptr, TYPE_MA); + pkt.next_trans = -1; /// continuum transition, no restrictions for further line interactions + pkt.emissiontype = get_emtype_continuum(element, *ion, lower, upperionlevel); + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; + pkt.nscatterings = 0; } -static void do_macroatom_ionisation(const int modelgridindex, const int element, int *ion, int *level, - const double epsilon_current, const double internal_up_higher) { +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 auto nne = grid::get_nne(modelgridindex); - int upper = -1; - /// Randomly select the occuring transition - const double zrand = rng_uniform(); + // Randomly select the occuring transition + const double targetrate = rng_uniform() * internal_up_higher; double rate = 0.; - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, *ion, *level); phixstargetindex++) { - upper = get_phixsupperlevel(element, *ion, *level, phixstargetindex); - // const double epsilon_trans = epsilon(element, *ion + 1, upper) - epsilon_current; + const int nphixstargets = get_nphixstargets(element, *ion, *level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const double epsilon_trans = get_phixs_threshold(element, *ion, *level, phixstargetindex); const double R = get_corrphotoioncoeff(element, *ion, *level, phixstargetindex, modelgridindex); const double C = col_ionization_ratecoeff(T_e, nne, element, *ion, *level, phixstargetindex, epsilon_trans); rate += (R + C) * epsilon_current; - if (zrand * internal_up_higher < rate) { - break; + if (rate > targetrate) { + // set the macroatom's new state + *level = get_phixsupperlevel(element, *ion, *level, phixstargetindex); + *ion += 1; + return; } } - if (zrand * internal_up_higher >= rate) { - printout( - "%s: From Z=%d ionstage %d level %d, could not select upper level to ionise to. zrand %g * internal_up_higher " - "%g >= rate %g\n", - __func__, get_atomicnumber(element), get_ionstage(element, *ion), *level, zrand, internal_up_higher, rate); - abort(); - } - - assert_always(upper >= 0); - /// and set the macroatom's new state - *ion += 1; - *level = upper; + assert_always(false); } -void do_macroatom(struct packet *pkt_ptr, const int timestep) +} // anonymous namespace + +void do_macroatom(Packet &pkt, const MacroAtomState &pktmastate) /// Material for handling activated macro atoms. { - const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); + const int modelgridindex = grid::get_cell_modelgridindex(pkt.where); + const auto nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_testmodeonly(nonemptymgi >= 0); 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::timesteps[timestep].mid; + const double t_mid = globals::timesteps[globals::timestep].mid; // printout("[debug] do MA\n"); @@ -355,111 +337,97 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) /// not sure whether this reduces the number of calculations, as number of grid cells /// is much larger than number of pellets (next question: connection to number of /// photons) - const int element = pkt_ptr->mastate.element; - int ion = pkt_ptr->mastate.ion; - int level = pkt_ptr->mastate.level; - - const int activatingline = pkt_ptr->mastate.activatingline; - if (pkt_ptr->absorptiontype > 0 && activatingline > 0 && activatingline != pkt_ptr->absorptiontype) { - printout("error: mismatched absorptiontype %d != activatingline = %d pkt last_event %d emissiontype %d\n", - pkt_ptr->absorptiontype, activatingline, pkt_ptr->last_event, pkt_ptr->emissiontype); - } + const int element = pktmastate.element; + int ion = pktmastate.ion; + int level = pktmastate.level; + + const int activatingline = pktmastate.activatingline; + assert_testmodeonly(pkt.absorptiontype < 0 || activatingline < 0 || activatingline == pkt.absorptiontype); const int ion_in = ion; const int level_in = level; - const double nu_cmf_in = pkt_ptr->nu_cmf; - const double nu_rf_in = pkt_ptr->nu_rf; + const double nu_cmf_in = pkt.nu_cmf; + const double nu_rf_in = pkt.nu_rf; if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_TOTAL, pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_TOTAL, pkt.e_cmf); } - int jumps = 0; - bool end_packet = false; while (!end_packet) { - // ionisinglevels = get_ionisinglevels(element,ion); - /// Set this here to 1 to overcome problems in cells which have zero population /// in some ionisation stage. This is possible because the dependence on the /// originating levels population cancels out in the macroatom transition probabilities /// which are based on detailed balance. - // printout("[debug] %s Z=%d ionstage %d level %d, jumps %d\n", __func__, get_atomicnumber(element), - // get_ionstage(element,ion), level, jumps); - 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_testmodeonly(globals::cellhistory[tid].cellnumber == modelgridindex); + auto &chlevel = globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level]; - 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); + { + const auto lock = + std::lock_guard(globals::mutex_cellcachemacroatom[get_uniquelevelindex(element, ion, level)]); + + assert_testmodeonly(globals::cellcache[cellcacheslotid].cellnumber == modelgridindex); + + /// If there are no precalculated rates available then calculate them + if (chlevel.processrates[MA_ACTION_INTERNALUPHIGHER] < 0) { + calculate_macroatom_transitionrates(modelgridindex, element, ion, level, t_mid, chlevel); + } } + const auto &processrates = chlevel.processrates; + // for debugging the transition rates: // { // printout("macroatom element %d ion %d level %d\n", element, ion, level); - // - // const char *actionlabel[MA_ACTION_COUNT] = { - // "MA_ACTION_RADDEEXC", "MA_ACTION_COLDEEXC", "MA_ACTION_RADRECOMB", - // "MA_ACTION_COLRECOMB", "MA_ACTION_INTERNALDOWNSAME", "MA_ACTION_INTERNALDOWNLOWER", - // "MA_ACTION_INTERNALUPSAME", "MA_ACTION_INTERNALUPHIGHER", "MA_ACTION_INTERNALUPHIGHERNT"}; - // for (enum ma_action action = 0; action < MA_ACTION_COUNT; action++) + // const char *actionlabel[MA_ACTION_COUNT] = { + // "MA_ACTION_RADDEEXC", "MA_ACTION_COLDEEXC", "MA_ACTION_RADRECOMB", + // "MA_ACTION_COLRECOMB", "MA_ACTION_INTERNALDOWNSAME", "MA_ACTION_INTERNALDOWNLOWER", + // "MA_ACTION_INTERNALUPSAME", "MA_ACTION_INTERNALUPHIGHER", "MA_ACTION_INTERNALUPHIGHERNT"}; + + // for (int action = 0; action < MA_ACTION_COUNT; action++) // printout("actions: %30s %g\n", actionlabel[action], processrates[action]); // } // select transition according to probabilities - double total_transitions = 0.; - for (int action = 0; action < MA_ACTION_COUNT; action++) { - total_transitions += processrates[action]; - } - assert_always(total_transitions > 0); - - enum ma_action selected_action = MA_ACTION_COUNT; - double zrand = rng_uniform(); - const double randomrate = zrand * total_transitions; - double rate = 0.; - for (int action = 0; action < MA_ACTION_COUNT; action++) { - rate += processrates[action]; - if (rate > randomrate) { - selected_action = static_cast(action); - break; - } - } + std::array cumulative_transitions{}; + std::partial_sum(processrates.begin(), processrates.end(), cumulative_transitions.begin()); + + const double randomrate = rng_uniform() * cumulative_transitions[MA_ACTION_COUNT - 1]; - assert_always(rate > randomrate); + // first cumulative_transitions[i] such that cumulative_transitions[i] > randomrate + const int selected_action = + std::distance(cumulative_transitions.cbegin(), + std::upper_bound(cumulative_transitions.cbegin(), cumulative_transitions.cend(), randomrate)); + + assert_always(selected_action < MA_ACTION_COUNT); + assert_always(cumulative_transitions[selected_action] > randomrate); switch (selected_action) { case MA_ACTION_RADDEEXC: { // printout("[debug] do_ma: radiative deexcitation\n"); - // printout("[debug] do_ma: jumps = %d\n", jumps); - do_macroatom_raddeexcitation(pkt_ptr, element, ion, level, processrates[MA_ACTION_RADDEEXC], activatingline); + do_macroatom_raddeexcitation(pkt, element, ion, level, activatingline); if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADDEEXC, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADDEEXC, pkt.e_cmf); stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_BOUNDBOUND_MACROATOM, - pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf); + pkt.e_cmf / H / pkt.nu_cmf); - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, pkt.e_cmf); } if constexpr (LOG_MACROATOM) { - fprintf(macroatom_file, "%8d %14d %2d %12d %12d %9d %9d %9d %11.5e %11.5e %11.5e %11.5e %9d\n", timestep, + fprintf(macroatom_file, "%8d %14d %2d %12d %12d %9d %9d %9d %11.5e %11.5e %11.5e %11.5e\n", globals::timestep, modelgridindex, get_atomicnumber(element), get_ionstage(element, ion_in), get_ionstage(element, ion), - level_in, level, activatingline, nu_cmf_in, pkt_ptr->nu_cmf, nu_rf_in, pkt_ptr->nu_rf, jumps); + level_in, level, activatingline, nu_cmf_in, pkt.nu_cmf, nu_rf_in, pkt.nu_rf); } end_packet = true; @@ -469,29 +437,25 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) case MA_ACTION_COLDEEXC: { /// collisional deexcitation of macro atom => convert the packet into a k-packet // printout("[debug] do_ma: collisional deexcitation\n"); - // printout("[debug] do_ma: jumps = %d\n", jumps); stats::increment(stats::COUNTER_MA_STAT_DEACTIVATION_COLLDEEXC); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 10; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 10; if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLDEEXC, - pkt_ptr->e_cmf); - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLDEEXC, pkt.e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, pkt.e_cmf); } - pkt_ptr->type = TYPE_KPKT; + pkt.type = TYPE_KPKT; end_packet = true; - safeadd(globals::colheatingestimator[modelgridindex], pkt_ptr->e_cmf); + atomicadd(globals::colheatingestimator[nonemptymgi], pkt.e_cmf); break; } case MA_ACTION_INTERNALDOWNSAME: { - pkt_ptr->interactions += 1; - jumps++; - level = do_macroatom_internal_down_same(element, ion, level, processrates[MA_ACTION_INTERNALDOWNSAME]); + stats::increment(stats::COUNTER_INTERACTIONS); + level = do_macroatom_internal_down_same(element, ion, level); break; } @@ -499,16 +463,15 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) case MA_ACTION_RADRECOMB: { /// Radiative recombination of MA: emitt a continuum-rpkt // printout("[debug] do_ma: radiative recombination\n"); - // printout("[debug] do_ma: jumps = %d\n", jumps); // printout("[debug] do_ma: element %d, ion %d, level %d\n", element, ion, level); if constexpr (TRACK_ION_STATS) { // stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADRECOMB, - // pkt_ptr->e_cmf); stats::increment_ion_stats(modelgridindex, element, ion, - // stats::ION_MACROATOM_ENERGYOUT_TOTAL, pkt_ptr->e_cmf); + // pkt.e_cmf); stats::increment_ion_stats(modelgridindex, element, ion, + // stats::ION_MACROATOM_ENERGYOUT_TOTAL, pkt.e_cmf); } - do_macroatom_radrecomb(pkt_ptr, modelgridindex, element, &ion, &level, processrates[MA_ACTION_RADRECOMB]); + do_macroatom_radrecomb(pkt, modelgridindex, element, &ion, &level, processrates[MA_ACTION_RADRECOMB]); end_packet = true; break; } @@ -516,35 +479,32 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) case MA_ACTION_COLRECOMB: { /// collisional recombination of macro atom => convert the packet into a k-packet // printout("[debug] do_ma: collisonal recombination\n"); - // printout("[debug] do_ma: jumps = %d\n",jumps); stats::increment(stats::COUNTER_MA_STAT_DEACTIVATION_COLLRECOMB); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 11; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 11; if constexpr (TRACK_ION_STATS) { stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLRECOMB, - pkt_ptr->e_cmf); - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, - pkt_ptr->e_cmf); + pkt.e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL, pkt.e_cmf); } - pkt_ptr->type = TYPE_KPKT; + pkt.type = TYPE_KPKT; end_packet = true; - safeadd(globals::colheatingestimator[modelgridindex], pkt_ptr->e_cmf); + atomicadd(globals::colheatingestimator[nonemptymgi], pkt.e_cmf); break; } case MA_ACTION_INTERNALDOWNLOWER: { // printout("[debug] do_ma: internal downward jump to lower ionstage\n"); - pkt_ptr->interactions += 1; - jumps++; + stats::increment(stats::COUNTER_INTERACTIONS); stats::increment(stats::COUNTER_MA_STAT_INTERNALDOWNLOWER); /// Randomly select the occuring transition - zrand = rng_uniform(); + const double targetrate = rng_uniform() * processrates[MA_ACTION_INTERNALDOWNLOWER]; // zrand = 1. - 1e-14; - rate = 0.; + double rate = 0.; // nlevels = get_nlevels(element,ion-1); const int nlevels = get_ionisinglevels(element, ion - 1); @@ -556,29 +516,27 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) const double R = rad_recombination_ratecoeff(T_e, nne, element, ion, level, lower, modelgridindex); const double C = col_recombination_ratecoeff(modelgridindex, element, ion, level, lower, epsilon_trans); rate += (R + C) * epsilon_target; - if (zrand * processrates[MA_ACTION_INTERNALDOWNLOWER] < rate) { + if (targetrate < rate) { break; } } /// and set the macroatom's new state if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, pkt.e_cmf); } ion -= 1; level = lower; if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, pkt.e_cmf); } if (lower >= nlevels) { printout("internal_down_lower %g\n", processrates[MA_ACTION_INTERNALDOWNLOWER]); - printout("abort at rate %g, zrand %g\n", rate, zrand); - abort(); + printout("abort at rate %g, targetrate %g\n", rate, targetrate); + std::abort(); } if (get_ionstage(element, ion) == 0 && lower == 0) { printout("internal downward transition to ground level occured ... abort\n"); @@ -586,22 +544,20 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) printout("Z %d, ionstage %d, energy %g\n", get_atomicnumber(element), get_ionstage(element, ion - 1), globals::elements[element].ions[ion - 1].levels[lower].epsilon); printout("[debug] do_ma: internal downward jump to lower ionstage\n"); - abort(); + std::abort(); } break; } case MA_ACTION_INTERNALUPSAME: { // printout("[debug] do_ma: internal upward jump within current ionstage\n"); - pkt_ptr->interactions += 1; - jumps++; + stats::increment(stats::COUNTER_INTERACTIONS); /// randomly select the occuring transition - zrand = rng_uniform(); const double *sum_internal_up_same = - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].sum_internal_up_same; + globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level].sum_internal_up_same; - const double targetval = zrand * processrates[MA_ACTION_INTERNALUPSAME]; + const double targetval = rng_uniform() * processrates[MA_ACTION_INTERNALUPSAME]; // first sum_internal_up_same[i] such that sum_internal_up_same[i] > targetval const double *const upperval = @@ -617,33 +573,29 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) case MA_ACTION_INTERNALUPHIGHER: { // printout("[debug] do_ma: internal upward jump to next ionstage\n"); - pkt_ptr->interactions += 1; - jumps++; + stats::increment(stats::COUNTER_INTERACTIONS); stats::increment(stats::COUNTER_MA_STAT_INTERNALUPHIGHER); if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, pkt.e_cmf); } do_macroatom_ionisation(modelgridindex, element, &ion, &level, epsilon_current, processrates[MA_ACTION_INTERNALUPHIGHER]); if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, pkt.e_cmf); } break; } case MA_ACTION_INTERNALUPHIGHERNT: { - pkt_ptr->interactions += 1; + stats::increment(stats::COUNTER_INTERACTIONS); // ion += 1; if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL, pkt.e_cmf); } ion = nonthermal::nt_random_upperion(modelgridindex, element, ion, false); @@ -651,28 +603,38 @@ void do_macroatom(struct packet *pkt_ptr, const int timestep) stats::increment(stats::COUNTER_MA_STAT_INTERNALUPHIGHERNT); if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL, pkt.e_cmf); } - // printout("Macroatom non-thermal ionisation to Z=%d ionstage %d level %d\n", get_atomicnumber(element), ion, - // level); break; } case MA_ACTION_COUNT: { printout("ERROR: Problem selecting MA_ACTION\n"); - abort(); + std::abort(); } + + default: + if constexpr (TESTMODE) { + printout("ERROR: Unknown macroatom selected_action type %d\n", selected_action); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); + } } - } /// endwhile + } - if (pkt_ptr->trueemissiontype == EMTYPE_NOTSET) { - pkt_ptr->trueemissiontype = pkt_ptr->emissiontype; - pkt_ptr->trueemissionvelocity = vec_len(pkt_ptr->em_pos) / pkt_ptr->em_time; - pkt_ptr->trueem_time = pkt_ptr->em_time; + // TODO Luke: we should probably only do this if the packet has become a r-packet, otherwise we should set + // trueemissiontype to EM_TYPE_NOTSET, but this method has already been published. If the difference is small for + // nebular Type Ias then just fix it. + if (pkt.trueemissiontype == EMTYPE_NOTSET) { + pkt.trueemissiontype = pkt.emissiontype; + pkt.trueemissionvelocity = vec_len(pkt.em_pos) / pkt.em_time; + pkt.trueem_time = pkt.em_time; } - /// procedure ends only after a change to r or k packets has taken place + if (pkt.type == TYPE_RPKT) { + vpkt_call_estimators(pkt, TYPE_MA); + } } /// Calculation of radiative rates /////////////////////////////////////////////////////// @@ -685,9 +647,9 @@ void macroatom_open_file(const int my_rank) { snprintf(filename, MAXFILENAMELENGTH, "macroatom_%.4d.out", my_rank); assert_always(macroatom_file == nullptr); macroatom_file = fopen_required(filename, "w"); - fprintf(macroatom_file, "%8s %14s %2s %12s %12s %9s %9s %9s %11s %11s %11s %11s %9s\n", "timestep", "modelgridindex", - "Z", "ionstage_in", "ionstage_out", "level_in", "level_out", "activline", "nu_cmf_in", "nu_cmf_out", - "nu_rf_in", "nu_rf_out", "jumps"); + fprintf(macroatom_file, "%8s %14s %2s %12s %12s %9s %9s %9s %11s %11s %11s %11s\n", "timestep", "modelgridindex", "Z", + "ionstage_in", "ionstage_out", "level_in", "level_out", "activline", "nu_cmf_in", "nu_cmf_out", "nu_rf_in", + "nu_rf_out"); } void macroatom_close_file() { @@ -707,7 +669,7 @@ auto rad_deexcitation_ratecoeff(const int modelgridindex, const int element, con const double n_u = get_levelpop(modelgridindex, element, ion, upper); const double n_l = get_levelpop(modelgridindex, element, ion, lower); - double R = 0.0; + double R = 0.; // if ((n_u > 1.1 * MINPOP) && (n_l > 1.1 * MINPOP)) { @@ -721,7 +683,7 @@ auto rad_deexcitation_ratecoeff(const int modelgridindex, const int element, con if (tau_sobolev > 1e-100) { const double beta = 1.0 / tau_sobolev * (-std::expm1(-tau_sobolev)); - // const double beta = 1.0; + // const double beta = 1.; R = A_ul * beta; } else { // printout("[warning] rad_deexcitation: tau_sobolev %g <= 0, set beta=1\n",tau_sobolev); @@ -729,7 +691,7 @@ auto rad_deexcitation_ratecoeff(const int modelgridindex, const int element, con // printout("[warning] rad_deexcitation: n_l %g, n_u %g, B_lu %g, B_ul %g\n",n_l,n_u,B_lu,B_ul); // printout("[warning] rad_deexcitation: T_e %g, T_R %g, W %g in model cell // %d\n",grid::get_Te(modelgridindex),get_TR(modelgridindex),get_W(modelgridindex),modelgridindex); - R = 0.0; + R = 0.; // printout("[fatal] rad_excitation: tau_sobolev <= 0 ... %g abort",tau_sobolev); // abort(); } @@ -753,56 +715,35 @@ auto rad_excitation_ratecoeff(const int modelgridindex, const int element, const const double n_u = get_levelpop(modelgridindex, element, ion, upper); const double n_l = get_levelpop(modelgridindex, element, ion, lower); - double R = 0.0; - // if ((n_u >= 1.1 * MINPOP) && (n_l >= 1.1 * MINPOP)) - { - const double nu_trans = epsilon_trans / H; - const double A_ul = globals::elements[element].ions[ion].levels[lower].uptrans[uptransindex].einstein_A; - const double B_ul = CLIGHTSQUAREDOVERTWOH / std::pow(nu_trans, 3) * A_ul; - const double B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; + const double nu_trans = epsilon_trans / H; + const double A_ul = globals::elements[element].ions[ion].levels[lower].uptrans[uptransindex].einstein_A; + const double B_ul = CLIGHTSQUAREDOVERTWOH / std::pow(nu_trans, 3) * A_ul; + const double B_lu = stat_weight(element, ion, upper) / stat_weight(element, ion, lower) * B_ul; - const double tau_sobolev = (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_current; + const double tau_sobolev = (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * t_current; - if (tau_sobolev > 1e-100) { - const double beta = 1.0 / tau_sobolev * (-std::expm1(-tau_sobolev)); + if (tau_sobolev > 1e-100) { + 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::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) { - const double Jb_lu = radfield::get_Jb_lu(modelgridindex, jblueindex); - const double R_Jb = R_over_J_nu * Jb_lu; - // const int contribcount = radfield_get_Jb_lu_contribcount(modelgridindex, jblueindex); - // const double R_radfield = R_over_J_nu * radfield::radfield(nu_trans, modelgridindex); - // const double linelambda = 1e8 * CLIGHT / nu_trans; - // printout("Using detailed rad excitation lambda %5.1f contribcont %d R(Jblue) %g R(radfield) %g R_Jb/R - // %g\n", - // linelambda, contribcount, R_Jb, R_radfield, R_Jb / R_radfield); - // printout(" (for transition Z=%02d ionstage %d lower %d upper %d)\n", - // get_atomicnumber(element), get_ionstage(element, ion), lower, upper); - R = R_Jb; - } + const double R_over_J_nu = n_l > 0. ? (B_lu - B_ul * n_u / n_l) * beta : B_lu * beta; + + if constexpr (DETAILED_LINE_ESTIMATORS_ON) { + if (!globals::lte_iteration) { + // check for a detailed line flux estimator to replace the binned/blackbody radiation field estimate + if (const int jblueindex = radfield::get_Jblueindex(lineindex); jblueindex >= 0) { + return R_over_J_nu * radfield::get_Jb_lu(modelgridindex, jblueindex); } } - } else { - // printout("[warning] rad_excitation: tau_sobolev %g <= 0, set beta=1\n",tau_sobolev); - // printout("[warning] rad_excitation: element %d, ion %d, upper %d, lower %d\n",element,ion,upper,lower); - // printout("[warning] rad_excitation: n_l %g, n_u %g, B_lu %g, B_ul %g\n",n_l,n_u,B_lu,B_ul); - // printout("[warning] rad_excitation: T_e %g, T_R %g, W - // %g\n",grid::get_Te(modelgridindex),get_TR(modelgridindex),get_W(modelgridindex)); - R = 0.; } + const double R = R_over_J_nu * radfield::radfield(nu_trans, modelgridindex); + assert_testmodeonly(R >= 0.); assert_testmodeonly(std::isfinite(R)); + return R; } - return R; + return 0.; } auto rad_recombination_ratecoeff(const float T_e, const float nne, const int element, const int upperion, @@ -815,7 +756,7 @@ auto rad_recombination_ratecoeff(const float T_e, const float nne, const int ele // if (upperionlevel > get_maxrecombininglevel(element, upperion)) // return 0.; - double R = 0.0; + double R = 0.; const int lowerion = upperion - 1; const int nphixstargets = get_nphixstargets(element, lowerion, lowerionlevel); for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { @@ -838,7 +779,7 @@ auto rad_recombination_ratecoeff(const float T_e, const float nne, const int ele auto stim_recombination_ratecoeff(const float nne, const int element, const int upperion, const int upper, const int lower, const int modelgridindex) -> double { - double R = 0.0; + double R = 0.; if constexpr (SEPARATE_STIMRECOMB) { const int nphixstargets = get_nphixstargets(element, upperion - 1, lower); @@ -874,7 +815,7 @@ auto col_recombination_ratecoeff(const int modelgridindex, const int element, co /// Seaton approximation: Mihalas (1978), eq.5-79, p.134 /// select gaunt factor according to ionic charge - double g = NAN; + double g{NAN}; if (ionstage - 1 == 1) { g = 0.1; } else if (ionstage - 1 == 2) { @@ -907,7 +848,7 @@ auto col_ionization_ratecoeff(const float T_e, const float nne, const int elemen /// Seaton approximation: Mihalas (1978), eq.5-79, p.134 /// select gaunt factor according to ionic charge - double g = NAN; + double g{NAN}; const int ionstage = get_ionstage(element, ion); if (ionstage == 1) { g = 0.1; @@ -931,14 +872,13 @@ 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, const struct level_transition &downtransition) -> double + int upper, const LevelTransition &downtransition) -> double // multiply by upper level population to get a rate per second { 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 = downtransition.coll_str; - double C = 0.; if (coll_str_thisline < 0) { const bool forbidden = downtransition.forbidden; if (!forbidden) // alternative: (coll_strength > -1.5) i.e. to catch -1 @@ -958,33 +898,28 @@ auto col_deexcitation_ratecoeff(const float T_e, const float nne, const double e // test = 0.276 * exp(fac1) * gsl_sf_expint_E1(fac1); /// crude approximation to the already crude Van-Regemorter formula - // double test = 0.276 * exp(fac1) * (-0.5772156649 - log(fac1)); + // double test = 0.276 * exp(fac1) * (-EULERGAMMA - log(fac1)); // double Gamma = (g_bar > test) ? g_bar : test; // optimisation const double gauntfac = - (eoverkt > 0.33421) ? g_bar : 0.276 * std::exp(eoverkt) * (-0.5772156649 - std::log(eoverkt)); + (eoverkt > 0.33421) ? g_bar : 0.276 * std::exp(eoverkt) * (-EULERGAMMA - std::log(eoverkt)); const double g_ratio = lowerstatweight / upperstatweight; - C = C_0 * 14.51039491 * nne * std::sqrt(T_e) * trans_osc_strength * std::pow(H_ionpot / epsilon_trans, 2) * - eoverkt * g_ratio * gauntfac; - } else // alterative: (coll_strength > -3.5) to catch -2 or -3 - { - // forbidden transitions: magnetic dipole, electric quadropole... - // could be Axelrod? or Maurer - C = nne * 8.629e-6 * 0.01 * lowerstatweight / std::sqrt(T_e); + return C_0 * 14.51039491 * nne * std::sqrt(T_e) * trans_osc_strength * std::pow(H_ionpot / epsilon_trans, 2) * + eoverkt * g_ratio * gauntfac; } - } else // positive values are treated as effective collision strengths - { - // from Osterbrock and Ferland, p51 - // statweight_target is LOWER LEVEL stat weight - C = nne * 8.629e-6 * coll_str_thisline / upperstatweight / std::sqrt(T_e); - // test test - // C = n_u * nne * 8.629e-6 * pow(T_e,-0.5) * 0.01 * statweight_target; + + // forbidden transitions: magnetic dipole, electric quadropole... + // could be Axelrod? or Maurer + return nne * 8.629e-6 * 0.01 * lowerstatweight / std::sqrt(T_e); } - return C; + // positive coll_str_thisline is treated as effective collision strength + + // from Osterbrock and Ferland, p51 + return nne * 8.629e-6 * coll_str_thisline / upperstatweight / std::sqrt(T_e); } auto col_excitation_ratecoeff(const float T_e, const float nne, int element, int ion, int lower, int uptransindex, @@ -1015,7 +950,7 @@ auto col_excitation_ratecoeff(const float T_e, const float nne, int element, int /// crude approximation to the already crude Van-Regemorter formula const double exp_eoverkt = exp(eoverkt); - const double test = 0.276 * exp_eoverkt * (-0.5772156649 - std::log(eoverkt)); + const double test = 0.276 * exp_eoverkt * (-EULERGAMMA - std::log(eoverkt)); const double Gamma = g_bar > test ? g_bar : test; C = C_0 * nne * std::sqrt(T_e) * 14.51039491 * trans_osc_strength * pow(H_ionpot / epsilon_trans, 2) * eoverkt / exp_eoverkt * Gamma; diff --git a/macroatom.h b/macroatom.h index f43b788ac..7e2479ba2 100644 --- a/macroatom.h +++ b/macroatom.h @@ -1,57 +1,38 @@ +#pragma once #ifndef MACROATOM_H #define MACROATOM_H #include -enum ma_action { - /// Radiative deexcitation rate from this level. - MA_ACTION_RADDEEXC = 0, - /// Collisional deexcitation rate from this level. - MA_ACTION_COLDEEXC = 1, - /// Radiative recombination from this level. - MA_ACTION_RADRECOMB = 2, - /// Collisional recombination rate from this level. - MA_ACTION_COLRECOMB = 3, - /// Rate for internal downward transitions to same ionisation stage. - MA_ACTION_INTERNALDOWNSAME = 4, - /// Rate for internal upward transitions to same ionisation stage. - MA_ACTION_INTERNALDOWNLOWER = 5, - /// Rate for internal downward transitions to lower ionisation stage. - MA_ACTION_INTERNALUPSAME = 6, - /// Rate for internal upward transitions to higher ionisation stage. - MA_ACTION_INTERNALUPHIGHER = 7, - /// Rate for internal upward transitions to higher ionisation stage due to non-thermal collisions. - MA_ACTION_INTERNALUPHIGHERNT = 8, - MA_ACTION_COUNT = 9, -}; - #include "atomic.h" #include "constants.h" +#include "globals.h" #include "packet.h" void macroatom_open_file(int my_rank); void macroatom_close_file(); -void do_macroatom(struct packet *pkt_ptr, int timestep); +void do_macroatom(Packet &pkt, const MacroAtomState &pktmastate); -auto rad_deexcitation_ratecoeff(int modelgridindex, int element, int ion, int upper, int lower, double epsilon_trans, - float A_ul, double upperstatweight, double t_current) -> double; -auto rad_excitation_ratecoeff(int modelgridindex, int element, int ion, int lower, int uptransindex, - double epsilon_trans, int lineindex, double t_current) -> double; -auto rad_recombination_ratecoeff(float T_e, float nne, int element, int upperion, int upperionlevel, int lowerionlevel, - int modelgridindex) -> double; -auto stim_recombination_ratecoeff(float nne, int element, int upperion, int upper, int lower, - int modelgridindex) -> double; +[[nodiscard]] auto rad_deexcitation_ratecoeff(int modelgridindex, int element, int ion, int upper, int lower, + double epsilon_trans, float A_ul, double upperstatweight, + double t_current) -> double; +[[nodiscard]] auto rad_excitation_ratecoeff(int modelgridindex, int element, int ion, int lower, int uptransindex, + double epsilon_trans, int lineindex, double t_current) -> double; +[[nodiscard]] auto rad_recombination_ratecoeff(float T_e, float nne, int element, int upperion, int upperionlevel, + int lowerionlevel, int modelgridindex) -> double; +[[nodiscard]] auto stim_recombination_ratecoeff(float nne, int element, int upperion, int upper, int lower, + int modelgridindex) -> double; -auto col_recombination_ratecoeff(int modelgridindex, int element, int upperion, int upper, int lower, - double epsilon_trans) -> double; -auto col_ionization_ratecoeff(float T_e, float nne, int element, int ion, int lower, int phixstargetindex, - double epsilon_trans) -> double; +[[nodiscard]] auto col_recombination_ratecoeff(int modelgridindex, int element, int upperion, int upper, int lower, + double epsilon_trans) -> double; +[[nodiscard]] auto col_ionization_ratecoeff(float T_e, float nne, int element, int ion, int lower, int phixstargetindex, + double epsilon_trans) -> double; -auto col_deexcitation_ratecoeff(float T_e, float nne, double epsilon_trans, int element, int ion, int upper, - const struct level_transition &downtransition) -> double; +[[nodiscard]] auto col_deexcitation_ratecoeff(float T_e, float nne, double epsilon_trans, int element, int ion, + int upper, const LevelTransition &downtransition) -> double; -auto col_excitation_ratecoeff(float T_e, float nne, int element, int ion, int lower, int uptransindex, - double epsilon_trans, double lowerstatweight) -> double; +[[nodiscard]] auto col_excitation_ratecoeff(float T_e, float nne, int element, int ion, int lower, int uptransindex, + double epsilon_trans, double lowerstatweight) -> double; #endif // MACROATOM_H diff --git a/md5.cc b/md5.cc index d8fe0b572..873722172 100644 --- a/md5.cc +++ b/md5.cc @@ -12,10 +12,9 @@ /*************************** HEADER FILES ***************************/ #include "md5.h" -#include - #include #include +#include #include "sn3d.h" diff --git a/md5.h b/md5.h index 0ca1c89d3..cbf60f759 100644 --- a/md5.h +++ b/md5.h @@ -6,6 +6,7 @@ * Details: Defines the API for the corresponding MD5 implementation. *********************************************************************/ +#pragma once #ifndef MD5_H #define MD5_H @@ -16,15 +17,15 @@ #define MD5_BLOCK_SIZE 16 // MD5 outputs a 16 byte digest /**************************** DATA TYPES ****************************/ -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines +using BYTE = unsigned char; // 8-bit byte +using WORD = unsigned int; // 32-bit word, change to "long" for 16-bit machines -typedef struct { +using MD5_CTX = struct { BYTE data[64]; WORD datalen; unsigned long long bitlen; WORD state[4]; -} MD5_CTX; +}; /*********************** FUNCTION DECLARATIONS **********************/ void md5_init(MD5_CTX *ctx); diff --git a/nltepop.cc b/nltepop.cc index a4fd570c4..df31818a1 100644 --- a/nltepop.cc +++ b/nltepop.cc @@ -1,30 +1,37 @@ #include "nltepop.h" #include -#include +#include +#include #include #include +#include #include +#include #include #include -#include +#include +#include +#include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" +#include "globals.h" #include "grid.h" #include "ltepop.h" #include "macroatom.h" #include "nonthermal.h" #include "ratecoeff.h" #include "sn3d.h" -#include "update_grid.h" -static FILE *nlte_file = nullptr; +static FILE *nlte_file{}; // can save memory by using a combined rate matrix at the cost of diagnostic information -constexpr bool individual_process_matricies = true; +static constexpr bool individual_process_matricies = true; -static inline auto get_nlte_vector_index(const int element, const int ion, const int level) -> int +static auto get_nlte_vector_index(const int element, const int ion, const int level) -> int // this is the index for the NLTE solver that is handling all ions of a single element // This is NOT an index into grid::modelgrid[modelgridindex].nlte_pops that contains all elements { @@ -81,14 +88,14 @@ static void filter_nlte_matrix(const int element, gsl_matrix *rate_matrix, gsl_v const gsl_matrix rate_matrix_var = *rate_matrix; const int nlte_dimension = rate_matrix_var.size1; for (int index = 0; index < nlte_dimension; index++) { - double row_max = 0.0; + double row_max = 0.; for (int column = 0; column < nlte_dimension; column++) { const double element_value = fabs(gsl_matrix_get(rate_matrix, index, column)); if (element_value > row_max) { row_max = element_value; } } - double col_max = 0.0; + double col_max = 0.; for (int row = 1; row < nlte_dimension; row++) // skip the normalisation row 0 { const double element_value = fabs(gsl_matrix_get(rate_matrix, row, index)); @@ -99,7 +106,7 @@ static void filter_nlte_matrix(const int element, gsl_matrix *rate_matrix, gsl_v int ion = -1; int level = -1; get_ion_level_of_nlte_vector_index(index, element, &ion, &level); - // printout("index%4d (ion_stage%2d level%4d) row_max %.1e col_max %.1e ", + // printout("index%4d (ionstage%2d level%4d) row_max %.1e col_max %.1e ", // index,get_ionstage(element,ion),level,row_max,col_max); if ((row_max < 1e-100) || (col_max < 1e-100)) { @@ -256,10 +263,9 @@ static void print_element_rates_summary(const int element, const int modelgridin for (int level = 0; (level < max_printed_levels) && (level < nlevels) && (level <= nlevels_nlte + 1); level++) { if (level == 0) { - printout( - " modelgridindex %d timestep %d NLTE iteration %d Te %g nne %g: NLTE summary for Z=%d ion_stage %d:\n", - modelgridindex, timestep, nlte_iter, grid::get_Te(modelgridindex), grid::get_nne(modelgridindex), - atomic_number, ionstage); + printout(" modelgridindex %d timestep %d NLTE iteration %d Te %g nne %g: NLTE summary for Z=%d ionstage %d:\n", + modelgridindex, timestep, nlte_iter, grid::get_Te(modelgridindex), grid::get_nne(modelgridindex), + atomic_number, ionstage); printout( " pop rates bb_rad bb_col bb_ntcol bf_rad bf_col " "bf_ntcol\n"); @@ -289,13 +295,13 @@ static void print_level_rates(const int modelgridindex, const int timestep, cons selected_level > (get_nlevels_nlte(element, selected_ion) + (ion_has_superlevel(element, selected_ion) ? 1 : 0))) { printout("print_level_rates: invalid element/ion/level arguments\n"); - abort(); + std::abort(); } if (rate_matrix_rad_bb == rate_matrix_coll_bb) { printout( "print_level_rates: rate_matrix_rad_bb == rate_matrix_coll_bb. check individual_process_matricies is off\n"); - abort(); + std::abort(); } const gsl_vector popvector = *popvec; @@ -305,7 +311,7 @@ static void print_level_rates(const int modelgridindex, const int timestep, cons const int selected_index = get_nlte_vector_index(element, selected_ion, selected_level); const double pop_selectedlevel = gsl_vector_get(popvec, selected_index); printout( - "timestep %d cell %d Te %g nne %g NLTE level diagnostics for Z=%d ion_stage %d level %d rates into and out of " + "timestep %d cell %d Te %g nne %g NLTE level diagnostics for Z=%d ionstage %d level %d rates into and out of " "this level\n", timestep, modelgridindex, grid::get_Te(modelgridindex), grid::get_nne(modelgridindex), atomic_number, selected_ionstage, selected_level); @@ -392,20 +398,14 @@ static void nltepop_reset_element(const int modelgridindex, const int element) { const int nions = get_nions(element); for (int ion = 0; ion < nions; ion++) { const int nlte_start = globals::elements[element].ions[ion].first_nlte; - const int nlevels_nlte = get_nlevels_nlte(element, ion); - for (int level = 1; level <= nlevels_nlte; level++) { - grid::modelgrid[modelgridindex].nlte_pops[nlte_start + level - 1] = -1.0; // flag to indicate no useful data - } - - if (ion_has_superlevel(element, ion)) { - grid::modelgrid[modelgridindex].nlte_pops[nlte_start + nlevels_nlte] = -1.0; - } + std::fill_n(&grid::modelgrid[modelgridindex].nlte_pops[nlte_start], + get_nlevels_nlte(element, ion) + (ion_has_superlevel(element, ion) ? 1 : 0), -1.); } } 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.); + auto superlevel_partfuncs = std::vector(nions, 0.); for (int ion = 0; ion < nions; ion++) { if (ion_has_superlevel(element, ion)) { const int nlevels_nlte = get_nlevels_nlte(element, ion); @@ -413,8 +413,6 @@ static auto get_element_superlevelpartfuncs(const int modelgridindex, const int for (int level = nlevels_nlte + 1; level < nlevels; level++) { superlevel_partfuncs[ion] += superlevel_boltzmann(modelgridindex, element, ion, level); } - } else { - superlevel_partfuncs[ion] = 0.; } } @@ -473,7 +471,7 @@ static void nltepop_matrix_add_boundbound(const int modelgridindex, const int el *gsl_matrix_ptr(rate_matrix_coll_bb, upper_index, upper_index) -= C; *gsl_matrix_ptr(rate_matrix_coll_bb, lower_index, upper_index) += C; if ((R < 0) || (C < 0)) { - printout(" WARNING: Negative de-excitation rate from ion_stage %d level %d to level %d\n", + printout(" WARNING: Negative de-excitation rate from ionstage %d level %d to level %d\n", get_ionstage(element, ion), level, lower); } } @@ -482,7 +480,7 @@ static void nltepop_matrix_add_boundbound(const int modelgridindex, const int el const int nuptrans = get_nuptrans(element, ion, level); for (int i = 0; i < nuptrans; i++) { const int lineindex = globals::elements[element].ions[ion].levels[level].uptrans[i].lineindex; - const struct linelist_entry *line = &globals::linelist[lineindex]; + const TransitionLine *line = &globals::linelist[lineindex]; const int upper = line->upperlevelindex; const double epsilon_trans = epsilon(element, ion, upper) - epsilon_level; @@ -501,17 +499,6 @@ static void nltepop_matrix_add_boundbound(const int modelgridindex, const int el nonthermal::nt_excitation_ratecoeff(modelgridindex, element, ion, level, i, epsilon_trans, lineindex) * s_renorm[level]; - // if ((Z == 26) && (ionstage == 1) && (level == 0) && (upper <= 5)) - // { - // const double tau_sobolev = get_tau_sobolev(modelgridindex, lineindex, t_mid); - // const double nu_trans = epsilon_trans / H; - // const double lambda = 1e8 * CLIGHT / nu_trans; // should be in Angstroms - // printout("Z=%d ionstage %d lower %d upper %d lambda %6.1fÃ… tau_sobolev=%g einstein_A %g osc_strength %g - // coll_str %g\n", - // Z, ionstage, level, upper, lambda, tau_sobolev, - // linelist[lineindex].einstein_A, linelist[lineindex].osc_strength, linelist[lineindex].coll_str); - // } - const int lower_index = level_index; const int upper_index = get_nlte_vector_index(element, ion, upper); @@ -539,13 +526,13 @@ static void nltepop_matrix_add_ionisation(const int modelgridindex, const int el const int maxrecombininglevel = get_maxrecombininglevel(element, ion + 1); for (int level = 0; level < nionisinglevels; level++) { - const int level_index = get_nlte_vector_index(element, ion, level); + const int lower_index = get_nlte_vector_index(element, ion, level); // thermal collisional ionization, photoionisation and recombination processes const double epsilon_current = epsilon(element, ion, level); - const int lower_index = level_index; - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); const int upper_index = get_nlte_vector_index(element, ion + 1, upper); const double epsilon_trans = epsilon(element, ion + 1, upper) - epsilon_current; @@ -563,7 +550,7 @@ static void nltepop_matrix_add_ionisation(const int modelgridindex, const int el *gsl_matrix_ptr(rate_matrix_coll_bf, upper_index, lower_index) += C_ionisation * s_renorm[level]; if ((R_ionisation < 0) || (C_ionisation < 0)) { - printout(" WARNING: Negative ionization rate from ion_stage %d level %d phixstargetindex %d\n", + printout(" WARNING: Negative ionization rate from ionstage %d level %d phixstargetindex %d\n", get_ionstage(element, ion), level, phixstargetindex); } @@ -580,7 +567,7 @@ static void nltepop_matrix_add_ionisation(const int modelgridindex, const int el *gsl_matrix_ptr(rate_matrix_coll_bf, lower_index, upper_index) += C_recomb * s_renorm[upper]; if ((R_recomb < 0) || (C_recomb < 0)) { - printout(" WARNING: Negative recombination rate to ion_stage %d level %d phixstargetindex %d\n", + printout(" WARNING: Negative recombination rate to ionstage %d level %d phixstargetindex %d\n", get_ionstage(element, ion), level, phixstargetindex); } } @@ -595,7 +582,7 @@ static void nltepop_matrix_add_nt_ionisation(const int modelgridindex, const int assert_always(ion + 1 < get_nions(element)); // can't ionise the top ion const double Y_nt = nonthermal::nt_ionization_ratecoeff(modelgridindex, element, ion); if (Y_nt < 0.) { - printout(" WARNING: Negative NT_ionization rate from ion_stage %d\n", get_ionstage(element, ion)); + printout(" WARNING: Negative NT_ionization rate from ionstage %d\n", get_ionstage(element, ion)); } const int nlevels = get_nlevels(element, ion); @@ -778,7 +765,7 @@ static auto nltepop_matrix_solve(const int element, const gsl_matrix *rate_matri int level = 0; get_ion_level_of_nlte_vector_index(row, element, &ion, &level); - // printout("index %4d (ion_stage %d level%4d): residual %+.2e recovered balance: %+.2e normed pop %.2e pop %.2e + // printout("index %4d (ionstage %d level%4d): residual %+.2e recovered balance: %+.2e normed pop %.2e pop %.2e // departure ratio %.4f\n", // row,get_ionstage(element,ion),level, gsl_vector_get(residual_vector,row), // recovered_balance_vector_elem, gsl_vector_get(x,row), @@ -787,7 +774,7 @@ static auto nltepop_matrix_solve(const int element, const gsl_matrix *rate_matri if (gsl_vector_get(popvec, row) < 0.0) { printout( - " WARNING: NLTE solver gave negative population to index %ud (Z=%d ion_stage %d level %d), pop = %g. " + " WARNING: NLTE solver gave negative population to index %zud (Z=%d ionstage %d level %d), pop = %g. " "Replacing with LTE pop of %g\n", row, get_atomicnumber(element), get_ionstage(element, ion), level, gsl_vector_get(x, row) * gsl_vector_get(pop_normfactor_vec, row), gsl_vector_get(pop_normfactor_vec, row)); @@ -821,7 +808,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const return; } - const time_t sys_time_start_nltesolver = time(nullptr); + const auto sys_time_start_nltesolver = std::time(nullptr); const double t_mid = globals::timesteps[timestep].mid; const int nions = get_nions(element); @@ -829,7 +816,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const printout( "Solving for NLTE populations in cell %d at timestep %d NLTE iteration %d for element Z=%d (mass fraction %.2e, " - "population %.2e)\n", + "nnelement %.2e cm^-3)\n", modelgridindex, timestep, nlte_iter, atomic_number, grid::get_elem_abundance(modelgridindex, element), nnelement); auto superlevel_partfunc = std::vector(nions) = get_element_superlevelpartfuncs(modelgridindex, element); @@ -838,12 +825,12 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const // printout("NLTE: the vector dimension is %d", nlte_dimension); gsl_matrix *rate_matrix = gsl_matrix_calloc(nlte_dimension, nlte_dimension); - gsl_matrix *rate_matrix_rad_bb = nullptr; - gsl_matrix *rate_matrix_coll_bb = nullptr; - gsl_matrix *rate_matrix_ntcoll_bb = nullptr; - gsl_matrix *rate_matrix_rad_bf = nullptr; - gsl_matrix *rate_matrix_coll_bf = nullptr; - gsl_matrix *rate_matrix_ntcoll_bf = nullptr; + gsl_matrix *rate_matrix_rad_bb{}; + gsl_matrix *rate_matrix_coll_bb{}; + gsl_matrix *rate_matrix_ntcoll_bb{}; + gsl_matrix *rate_matrix_rad_bf{}; + gsl_matrix *rate_matrix_coll_bf{}; + gsl_matrix *rate_matrix_ntcoll_bf{}; if (individual_process_matricies) { rate_matrix_rad_bb = gsl_matrix_calloc(nlte_dimension, nlte_dimension); rate_matrix_coll_bb = gsl_matrix_calloc(nlte_dimension, nlte_dimension); @@ -875,7 +862,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const auto s_renorm = std::vector(nlevels); for (int level = 0; level <= nlevels_nlte; level++) { - s_renorm[level] = 1.0; + s_renorm[level] = 1.; } for (int level = (nlevels_nlte + 1); level < nlevels; level++) { @@ -977,17 +964,17 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const 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); - // const int ion_stage = get_ionstage(element, ion); - // printout(" [ion_stage %d]\n", ion_stage); + // const int ionstage = get_ionstage(element, ion); + // printout(" [ionstage %d]\n", ionstage); // - // printout(" For ion_stage %d, the ground state populations are %g (function) and %g (matrix result with + // printout(" For ionstage %d, the ground state populations are %g (function) and %g (matrix result with // normed pop %g, ltepopnormfactor %g)\n",get_ionstage(element,ion), // get_groundlevelpop(modelgridindex, element, ion), gsl_vector_get(popvec, index_gs), // gsl_vector_get(x, index_gs), gsl_vector_get(pop_norm_factor_vec, index_gs)); // store the NLTE level populations const int nlte_start = globals::elements[element].ions[ion].first_nlte; - // double solution_ion_pop = 0.0; + // double solution_ion_pop = 0.; for (int level = 1; level <= nlevels_nlte; level++) { const int index = get_nlte_vector_index(element, ion, level); grid::modelgrid[modelgridindex].nlte_pops[nlte_start + level - 1] = @@ -1068,7 +1055,7 @@ void solve_nlte_pops_element(const int element, const int modelgridindex, const gsl_matrix_free(rate_matrix); gsl_vector_free(balance_vector); gsl_vector_free(pop_norm_factor_vec); - const int duration_nltesolver = time(nullptr) - sys_time_start_nltesolver; + const int duration_nltesolver = std::time(nullptr) - sys_time_start_nltesolver; if (duration_nltesolver > 2) { printout("NLTE population solver call for Z=%d took %d seconds\n", get_atomicnumber(element), duration_nltesolver); } @@ -1089,8 +1076,7 @@ void nltepop_open_file(const int my_rank) { snprintf(filename, MAXFILENAMELENGTH, "nlte_%.4d.out", my_rank); assert_always(nlte_file == nullptr); nlte_file = fopen_required(filename, "w"); - fprintf(nlte_file, "%8s %14s %2s %9s %5s %11s %11s %11s\n", "timestep", "modelgridindex", "Z", "ion_stage", "level", - "n_LTE", "n_NLTE", "ion_popfrac"); + fprintf(nlte_file, "timestep modelgridindex Z ionstage level n_LTE n_NLTE ion_popfrac\n"); } void nltepop_close_file() { @@ -1110,22 +1096,24 @@ void nltepop_write_to_file(const int modelgridindex, const int timestep) { // timestep, n, grid::get_TR(n), grid::get_Te(n), grid::get_W(n), grid::get_TJ(n), grid::get_nne(n)); for (int element = 0; element < get_nelements(); element++) { + if (!elem_has_nlte_levels(element)) { + continue; + } const int nions = get_nions(element); const int atomic_number = get_atomicnumber(element); for (int ion = 0; ion < nions; ion++) { const int nlevels_nlte = get_nlevels_nlte(element, ion); const int ion_first_nlte = globals::elements[element].ions[ion].first_nlte; - const int ion_stage = get_ionstage(element, ion); + const int ionstage = get_ionstage(element, ion); const int nsuperlevels = ion_has_superlevel(element, ion) ? 1 : 0; for (int level = 0; level <= nlevels_nlte + nsuperlevels; level++) { double nnlevellte = calculate_levelpop_lte(modelgridindex, element, ion, level); - double nnlevelnlte = NAN; + double nnlevelnlte{NAN}; - // use "%8d %14d %2d %9d " for fixed width - fprintf(nlte_file, "%d %d %d %d ", timestep, modelgridindex, atomic_number, ion_stage); + fprintf(nlte_file, "%d %d %d %d ", timestep, modelgridindex, atomic_number, ionstage); if (level <= nlevels_nlte) { fprintf(nlte_file, "%d ", level); @@ -1157,7 +1145,7 @@ void nltepop_write_to_file(const int modelgridindex, const int timestep) { } const double ion_popfrac = nnlevelnlte / get_nnion(modelgridindex, element, ion); - fprintf(nlte_file, "%11.5e %11.5e %11.5e\n", nnlevellte, nnlevelnlte, ion_popfrac); + fprintf(nlte_file, "%.5e %.5e %.5e\n", nnlevellte, nnlevelnlte, ion_popfrac); } } } @@ -1197,7 +1185,7 @@ void nltepop_read_restart_data(FILE *restart_file) { assert_always(fscanf(restart_file, "%d\n", &code_check) == 1); if (code_check != 75618527) { printout("ERROR: Beginning of NLTE restart data not found!\n"); - abort(); + std::abort(); } int total_nlte_levels_in = 0; @@ -1205,7 +1193,7 @@ void nltepop_read_restart_data(FILE *restart_file) { if (total_nlte_levels_in != globals::total_nlte_levels) { printout("ERROR: Expected %d NLTE levels but found %d in restart file\n", globals::total_nlte_levels, total_nlte_levels_in); - abort(); + std::abort(); } for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { @@ -1214,7 +1202,7 @@ void nltepop_read_restart_data(FILE *restart_file) { assert_always(fscanf(restart_file, "%d %la\n", &mgi_in, &grid::modelgrid[modelgridindex].totalcooling) == 2); if (mgi_in != modelgridindex) { printout("ERROR: expected data for cell %d but found cell %d\n", modelgridindex, mgi_in); - abort(); + std::abort(); } for (int element = 0; element < get_nelements(); element++) { @@ -1227,7 +1215,7 @@ void nltepop_read_restart_data(FILE *restart_file) { &grid::modelgrid[modelgridindex].cooling_contrib_ion[element][ion]) == 4); if (ion_in != ion) { printout("ERROR: expected data for ion %d but found ion %d\n", ion, ion_in); - abort(); + std::abort(); } } } diff --git a/nltepop.h b/nltepop.h index 9b1d9aec4..ad0767c3b 100644 --- a/nltepop.h +++ b/nltepop.h @@ -1,10 +1,11 @@ +#pragma once #ifndef NLTEPOP_H #define NLTEPOP_H #include void solve_nlte_pops_element(int element, int modelgridindex, int timestep, int nlte_iter); -auto superlevel_boltzmann(int modelgridindex, int element, int ion, int level) -> double; +[[nodiscard]] auto superlevel_boltzmann(int modelgridindex, int element, int ion, int level) -> double; void nltepop_write_to_file(int modelgridindex, int timestep); void nltepop_open_file(int my_rank); void nltepop_close_file(); diff --git a/nonthermal.cc b/nonthermal.cc index 28b57c94f..bf65ce02d 100644 --- a/nonthermal.cc +++ b/nonthermal.cc @@ -1,20 +1,34 @@ #include "nonthermal.h" +#ifdef MPI_ON +#include +#endif + #include -#include +#include #include #include -#include +#include #include #include +#include #include +#include +#include +#include +#include +#include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "ltepop.h" #include "macroatom.h" +#include "packet.h" #include "sn3d.h" #include "stats.h" @@ -36,10 +50,10 @@ namespace nonthermal { // const double nntot = get_nnion_tot(modelgridindex); // if (get_atomicnumber(element) == 8) // { -// const int ion_stage = get_ionstage(element, ion); -// if (ion_stage == 1) +// const int ionstage = get_ionstage(element, ion); +// if (ionstage == 1) // return 0.99 * nntot; -// else if (ion_stage == 2) +// else if (ionstage == 2) // return 0.01 * nntot; // } // return 0.; @@ -54,21 +68,21 @@ constexpr bool STORE_NT_SPECTRUM = false; // if this is on, the non-thermal ene // 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; +static constexpr double minionfraction = 1.e-8; // minimum deposition rate density (eV/s/cm^3) to solve SF equation -constexpr double MINDEPRATE = 0.; +static constexpr double MINDEPRATE = 0.; // Bohr radius squared in cm^2 -constexpr double A_naught_squared = 2.800285203e-17; +static constexpr double A_naught_squared = 2.800285203e-17; // specifies max number of shells for which data is known for computing mean binding energies -constexpr int M_NT_SHELLS = 10; +static constexpr int M_NT_SHELLS = 10; // maximum number of elements for which binding energy tables are to be used -constexpr int MAX_Z_BINDING = 30; +static constexpr int MAX_Z_BINDING = 30; -static double electron_binding[MAX_Z_BINDING][M_NT_SHELLS]; +static std::array, MAX_Z_BINDING> electron_binding; struct collionrow { int Z; @@ -88,9 +102,9 @@ struct collionrow { float n_auger_elec_avg; }; -static std::vector colliondata; +static std::vector colliondata; -static FILE *nonthermalfile = nullptr; +static FILE *nonthermalfile{}; static bool nonthermal_initialized = false; static gsl_vector *envec; // energy grid on which solution is sampled @@ -99,10 +113,10 @@ static gsl_vector *sourcevec; // samples of the source function (energy distrib static double E_init_ev = 0; // the energy injection rate density (and mean energy of injected electrons if source integral is one) in eV -constexpr double DELTA_E = (SF_EMAX - SF_EMIN) / (SFPTS - 1); +static constexpr double DELTA_E = (SF_EMAX - SF_EMIN) / (SFPTS - 1); // Monte Carlo result - compare to analytical expectation -double nt_energy_deposited; +static double nt_energy_deposited; struct nt_excitation_struct { double frac_deposition; // the fraction of the non-thermal deposition energy going to the excitation transition @@ -112,28 +126,28 @@ struct nt_excitation_struct { }; struct nt_solution_struct { - double *yfunc = nullptr; // Samples of the Spencer-Fano solution function. Multiply by energy to get non-thermal - // electron number flux. y(E) * dE is the flux of electrons with energy in the range (E, E + - // dE) y has units of particles / cm2 / s / eV + double *yfunc{}; // Samples of the Spencer-Fano solution function. Multiply by energy to get non-thermal + // electron number flux. y(E) * dE is the flux of electrons with energy in the range (E, E + + // dE) y has units of particles / cm2 / s / eV float frac_heating = 1.; // energy fractions should add up to 1.0 if the solution is good float frac_ionization = 0.; // fraction of deposition energy going to ionization float frac_excitation = 0.; // fraction of deposition energy going to excitation // these points arrays of length includedions - float *eff_ionpot = nullptr; // these are used to calculate the non-thermal ionization rate + float *eff_ionpot{}; // these are used to calculate the non-thermal ionization rate double *fracdep_ionization_ion = nullptr; // the fraction of the non-thermal deposition energy going to ionizing this ion // these point to arrays of length includedions * (NT_MAX_AUGER_ELECTRONS + 1) - float *prob_num_auger = nullptr; // probability that one ionisation of this ion will produce n Auger electrons. - // elements sum to 1.0 for a given ion - float *ionenfrac_num_auger = nullptr; // like above, but energy weighted. elements sum to 1.0 for an ion + float *prob_num_auger{}; // probability that one ionisation of this ion will produce n Auger electrons. + // elements sum to 1.0 for a given ion + float *ionenfrac_num_auger{}; // like above, but energy weighted. elements sum to 1.0 for an ion - std::vector frac_excitations_list; + std::vector frac_excitations_list; - int timestep_last_solved = -1; // the quantities above were calculated for this timestep - float nneperion_when_solved = NAN; // the nne when the solver was last run + int timestep_last_solved = -1; // the quantities above were calculated for this timestep + float nneperion_when_solved{NAN}; // the nne when the solver was last run }; static struct nt_solution_struct *nt_solution; @@ -149,7 +163,7 @@ static void read_binding_energies() { assert_always(fscanf(binding, "%d %d", &dum1, &dum2) == 2); // dimensions of the table if ((dum1 != M_NT_SHELLS) || (dum2 != MAX_Z_BINDING)) { printout("Wrong size for the binding energy tables!\n"); - abort(); + std::abort(); } for (int index1 = 0; index1 < dum2; index1++) { @@ -210,7 +224,7 @@ static void check_auger_probabilities(int modelgridindex) { } if (problem_found) { - abort(); + std::abort(); } } @@ -354,7 +368,7 @@ static void read_collion_data() { assert_always(colliondatacount > 0); for (int i = 0; i < colliondatacount; i++) { - struct collionrow collionrow {}; + collionrow collionrow{}; assert_always(fscanf(cifile, "%2d %2d %1d %1d %lg %lg %lg %lg %lg", &collionrow.Z, &collionrow.nelec, &collionrow.n, &collionrow.l, &collionrow.ionpot_ev, &collionrow.A, &collionrow.B, &collionrow.C, &collionrow.D) == 9); @@ -379,7 +393,7 @@ static void read_collion_data() { // printout("ci row: %2d %2d %1d %1d %lg %lg %lg %lg %lg\n", collionrow.Z, collionrow.nelec, collionrow.n, // collionrow.l, collionrow.ionpot_ev, collionrow.A, collionrow.B, collionrow.C, collionrow.D); } - printout("Stored %d of %d input shell cross sections\n", colliondata.size(), colliondatacount); + printout("Stored %zu of %d input shell cross sections\n", colliondata.size(), colliondatacount); fclose(cifile); @@ -449,20 +463,18 @@ void init(const int my_rank, const int ndo_nonempty) { char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "nonthermalspec_%.4d.out", my_rank); nonthermalfile = fopen_required(filename, "w"); - fprintf(nonthermalfile, "%8s %15s %8s %11s %11s %11s\n", "timestep", "modelgridindex", "index", "energy_ev", - "source", "y"); + fprintf(nonthermalfile, "timestep modelgridindex index energy_ev source y\n"); fflush(nonthermalfile); } - nt_solution = - static_cast(calloc(grid::get_npts_model(), sizeof(struct nt_solution_struct))); + nt_solution = static_cast(calloc(grid::get_npts_model(), sizeof(nt_solution_struct))); size_t mem_usage_yfunc = 0; for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { // should make these negative? nt_solution[modelgridindex].frac_heating = 0.97; nt_solution[modelgridindex].frac_ionization = 0.03; - nt_solution[modelgridindex].frac_excitation = 0.0; + nt_solution[modelgridindex].frac_excitation = 0.; nt_solution[modelgridindex].nneperion_when_solved = -1.; nt_solution[modelgridindex].timestep_last_solved = -1; @@ -552,7 +564,8 @@ void init(const int my_rank, const int ndo_nonempty) { void calculate_deposition_rate_density(const int modelgridindex, const int timestep) // should be in erg / s / cm^3 { - const double gamma_deposition = globals::rpkt_emiss[modelgridindex] * FOURPI; + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const double gamma_deposition = globals::dep_estimator_gamma[nonemptymgi] * FOURPI; const double tmid = globals::timesteps[timestep].mid; const double rho = grid::get_rho(modelgridindex); @@ -599,7 +612,7 @@ static auto get_y_sample(const int modelgridindex, const int index) -> double { } printout("non-thermal: attempted to get y function sample index %d in cell %d, but the y array pointer is null\n", index, modelgridindex); - abort(); + std::abort(); return -1; } @@ -610,7 +623,7 @@ static void nt_write_to_file(const int modelgridindex, const int timestep, const #endif if (!nonthermal_initialized || nonthermalfile == nullptr) { printout("Call to nonthermal_write_to_file before nonthermal_init"); - abort(); + std::abort(); } static long nonthermalfile_offset_iteration_zero = 0; @@ -633,8 +646,8 @@ static void nt_write_to_file(const int modelgridindex, const int timestep, const #endif for (int s = 0; s < SFPTS; s++) { - fprintf(nonthermalfile, "%8d %15d %8d %11.5e %11.5e %11.5e\n", timestep, modelgridindex, s, - gsl_vector_get(envec, s), gsl_vector_get(sourcevec, s), yscalefactor * get_y_sample(modelgridindex, s)); + fprintf(nonthermalfile, "%d %d %d %.5e %.5e %.5e\n", timestep, modelgridindex, s, gsl_vector_get(envec, s), + gsl_vector_get(sourcevec, s), yscalefactor * get_y_sample(modelgridindex, s)); } fflush(nonthermalfile); #ifdef _OPENMP @@ -746,8 +759,7 @@ constexpr auto electron_loss_rate(const double energy, const double nne) -> doub return boostfactor * nne * 2 * PI * pow(QE, 4) / energy * log(2 * energy / zetae); } const double v = sqrt(2 * energy / ME); - const double eulergamma = 0.577215664901532; - return boostfactor * nne * 2 * PI * pow(QE, 4) / energy * log(ME * pow(v, 3) / (eulergamma * pow(QE, 2) * omegap)); + return boostfactor * nne * 2 * PI * pow(QE, 4) / energy * log(ME * pow(v, 3) / (EULERGAMMA * pow(QE, 2) * omegap)); } constexpr auto xs_excitation(const int element, const int ion, const int lower, const int uptransindex, @@ -848,7 +860,7 @@ static auto get_xs_excitation_vector(gsl_vector *const xs_excitation_vec, const return -1; } -constexpr auto xs_impactionization(const double energy_ev, const struct collionrow &colliondata) -> double +constexpr auto xs_impactionization(const double energy_ev, const collionrow &colliondata) -> double // impact ionization cross section in cm^2 // energy and ionization_potential should be in eV // fitting forumula of Younger 1981 @@ -868,7 +880,7 @@ constexpr auto xs_impactionization(const double energy_ev, const struct collionr return 1e-14 * (A * (1 - 1 / u) + B * pow((1 - 1 / u), 2) + C * log(u) + D * log(u) / u) / (u * pow(ionpot_ev, 2)); } -static auto get_xs_ionization_vector(gsl_vector *const xs_vec, const struct collionrow &colliondata) -> int +static auto get_xs_ionization_vector(gsl_vector *const xs_vec, const collionrow &colliondata) -> int // xs_vec will be set with impact ionization cross sections for E > ionpot_ev (and zeros below this energy) { const double ionpot_ev = colliondata.ionpot_ev; @@ -921,7 +933,7 @@ static auto get_J(const int Z, const int ionstage, const double ionpot_ev) -> do return 24.2; } if (Z == 18) { // Ar I - return 10.0; + return 10.; } } @@ -978,7 +990,7 @@ static auto N_e(const int modelgridindex, const double energy) -> double 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); - const double lambda = fmin(SF_EMAX - energy_ev, energy_ev + ionpot_ev); + const double lambda = std::min(SF_EMAX - energy_ev, energy_ev + ionpot_ev); const int integral1startindex = get_energyindex_ev_lteq(ionpot_ev); const int integral1stopindex = get_energyindex_ev_lteq(lambda); @@ -1036,6 +1048,7 @@ static auto calculate_frac_heating(const int modelgridindex) -> float double N_e_contrib = 0.; // third term (integral from zero to SF_EMIN) const int nsteps = static_cast(ceil(SF_EMIN / DELTA_E) * 10); + assert_always(nsteps > 0); const double delta_endash = SF_EMIN / nsteps; for (int j = 0; j < nsteps; j++) { const double endash = SF_EMIN * j / nsteps; @@ -1048,7 +1061,7 @@ static auto calculate_frac_heating(const int modelgridindex) -> float if (!std::isfinite(frac_heating) || frac_heating < 0 || frac_heating > 1.0) { printout("WARNING: calculate_frac_heating: invalid result of %g. Setting to 1.0 instead\n", frac_heating); - return 1.0; + return 1.; } return frac_heating; @@ -1079,7 +1092,7 @@ static auto get_nt_frac_ionization(const int modelgridindex) -> float { if (frac_ionization < 0 || !std::isfinite(frac_ionization)) { printout("ERROR: get_nt_frac_ionization called with no valid solution stored for cell %d. frac_ionization = %g\n", modelgridindex, frac_ionization); - abort(); + std::abort(); } return frac_ionization; @@ -1095,127 +1108,118 @@ static auto get_nt_frac_excitation(const int modelgridindex) -> float { if (frac_excitation < 0 || !std::isfinite(frac_excitation)) { printout("ERROR: get_nt_frac_excitation called with no valid solution stored for cell %d. frac_excitation = %g\n", modelgridindex, frac_excitation); - abort(); + std::abort(); } return frac_excitation; } static auto get_mean_binding_energy(const int element, const int ion) -> double { - int q[M_NT_SHELLS]; - double total = NAN; - const int ioncharge = get_ionstage(element, ion) - 1; const int nbound = get_atomicnumber(element) - ioncharge; // number of bound electrons - if (nbound > 0) { - for (int &i : q) { - i = 0; - } + if (nbound <= 0) { + return 0.; + } - for (int electron_loop = 0; electron_loop < nbound; electron_loop++) { - if (q[0] < 2) // K 1s + std::array q{}; + std::fill(q.begin(), q.end(), 0); + + for (int electron_loop = 0; electron_loop < nbound; electron_loop++) { + if (q[0] < 2) // K 1s + { + q[0]++; + } else if (q[1] < 2) // L1 2s + { + q[1]++; + } else if (q[2] < 2) // L2 2p[1/2] + { + q[2]++; + } else if (q[3] < 4) // L3 2p[3/2] + { + q[3]++; + } else if (q[4] < 2) // M1 3s + { + q[4]++; + } else if (q[5] < 2) // M2 3p[1/2] + { + q[5]++; + } else if (q[6] < 4) // M3 3p[3/2] + { + q[6]++; + } else if (ioncharge == 0) { + if (q[9] < 2) // N1 4s { - q[0]++; - } else if (q[1] < 2) // L1 2s + q[9]++; + } else if (q[7] < 4) // M4 3d[3/2] { - q[1]++; - } else if (q[2] < 2) // L2 2p[1/2] + q[7]++; + } else if (q[8] < 6) // M5 3d[5/2] { - q[2]++; - } else if (q[3] < 4) // L3 2p[3/2] + q[8]++; + } else { + printout("Going beyond the 4s shell in NT calculation. Abort!\n"); + std::abort(); + } + } else if (ioncharge == 1) { + if (q[9] < 1) // N1 4s { - q[3]++; - } else if (q[4] < 2) // M1 3s + q[9]++; + } else if (q[7] < 4) // M4 3d[3/2] { - q[4]++; - } else if (q[5] < 2) // M2 3p[1/2] + q[7]++; + } else if (q[8] < 6) // M5 3d[5/2] { - q[5]++; - } else if (q[6] < 4) // M3 3p[3/2] + q[8]++; + } else { + printout("Going beyond the 4s shell in NT calculation. Abort!\n"); + std::abort(); + } + } else if (ioncharge > 1) { + if (q[7] < 4) // M4 3d[3/2] { - q[6]++; - } else if (ioncharge == 0) { - if (q[9] < 2) // N1 4s - { - q[9]++; - } else if (q[7] < 4) // M4 3d[3/2] - { - q[7]++; - } else if (q[8] < 6) // M5 3d[5/2] - { - q[8]++; - } else { - printout("Going beyond the 4s shell in NT calculation. Abort!\n"); - abort(); - } - } else if (ioncharge == 1) { - if (q[9] < 1) // N1 4s - { - q[9]++; - } else if (q[7] < 4) // M4 3d[3/2] - { - q[7]++; - } else if (q[8] < 6) // M5 3d[5/2] - { - q[8]++; - } else { - printout("Going beyond the 4s shell in NT calculation. Abort!\n"); - abort(); - } - } else if (ioncharge > 1) { - if (q[7] < 4) // M4 3d[3/2] - { - q[7]++; - } else if (q[8] < 6) // M5 3d[5/2] - { - q[8]++; - } else { - printout("Going beyond the 4s shell in NT calculation. Abort!\n"); - abort(); - } + q[7]++; + } else if (q[8] < 6) // M5 3d[5/2] + { + q[8]++; + } else { + printout("Going beyond the 4s shell in NT calculation. Abort!\n"); + std::abort(); } } + } - // printout("For element %d ion %d I got q's of: %d %d %d %d %d %d %d %d %d %d\n", element, ion, q[0], q[1], - // q[2], q[3], q[4], q[5], q[6], q[7], q[8], q[9]); - // printout("%g %g %g %g %g %g %g %g %g %g\n", electron_binding[get_atomicnumber(element)-1][0], - // electron_binding[get_atomicnumber(element)-1][1], - // electron_binding[get_atomicnumber(element)-1][2],electron_binding[get_atomicnumber(element)-1][3],electron_binding[get_atomicnumber(element)-1][4],electron_binding[get_atomicnumber(element)-1][5],electron_binding[get_atomicnumber(element)-1][6],electron_binding[get_atomicnumber(element)-1][7],electron_binding[get_atomicnumber(element)-1][8],electron_binding[get_atomicnumber(element)-1][9]); - - total = 0.0; - for (int electron_loop = 0; electron_loop < M_NT_SHELLS; electron_loop++) { - const double electronsinshell = q[electron_loop]; - if ((electronsinshell) > 0) { - double use2 = electron_binding[get_atomicnumber(element) - 1][electron_loop]; - const double use3 = globals::elements[element].ions[ion].ionpot; - if (use2 <= 0) { - use2 = electron_binding[get_atomicnumber(element) - 1][electron_loop - 1]; - // to get total += electronsinshell/electron_binding[get_atomicnumber(element)-1][electron_loop-1]; - // set use3 = 0. - if (electron_loop != 8) { - // For some reason in the Lotz data, this is no energy for the M5 shell before Ni. So if the complaint - // is for 8 (corresponding to that shell) then just use the M4 value - printout("Huh? I'm trying to use a binding energy when I have no data. element %d ion %d\n", element, ion); - printout("Z = %d, ion_stage = %d\n", get_atomicnumber(element), get_ionstage(element, ion)); - abort(); - } - } - if (use2 < use3) { - total += electronsinshell / use3; - } else { - total += electronsinshell / use2; - } + // printout("For element %d ion %d I got q's of: %d %d %d %d %d %d %d %d %d %d\n", element, ion, q[0], q[1], + // q[2], q[3], q[4], q[5], q[6], q[7], q[8], q[9]); + // printout("%g %g %g %g %g %g %g %g %g %g\n", electron_binding[get_atomicnumber(element)-1][0], + // electron_binding[get_atomicnumber(element)-1][1], + // electron_binding[get_atomicnumber(element)-1][2],electron_binding[get_atomicnumber(element)-1][3],electron_binding[get_atomicnumber(element)-1][4],electron_binding[get_atomicnumber(element)-1][5],electron_binding[get_atomicnumber(element)-1][6],electron_binding[get_atomicnumber(element)-1][7],electron_binding[get_atomicnumber(element)-1][8],electron_binding[get_atomicnumber(element)-1][9]); + + double total = 0.; + for (int electron_loop = 0; electron_loop < M_NT_SHELLS; electron_loop++) { + const int electronsinshell = q[electron_loop]; + if (electronsinshell <= 0) { + continue; + } + double enbinding = electron_binding[get_atomicnumber(element) - 1][electron_loop]; + const double ionpot = globals::elements[element].ions[ion].ionpot; + if (enbinding <= 0) { + enbinding = electron_binding[get_atomicnumber(element) - 1][electron_loop - 1]; + // to get total += electronsinshell/electron_binding[get_atomicnumber(element)-1][electron_loop-1]; + // set use3 = 0. + if (electron_loop != 8) { + // For some reason in the Lotz data, this is no energy for the M5 shell before Ni. So if the complaint + // is for 8 (corresponding to that shell) then just use the M4 value + printout("Huh? I'm trying to use a binding energy when I have no data. element %d ion %d\n", element, ion); + printout("Z = %d, ionstage = %d\n", get_atomicnumber(element), get_ionstage(element, ion)); + std::abort(); } - // printout("total %g\n", total); } + total += electronsinshell / std::max(ionpot, enbinding); - } else { - total = 0.0; + // printout("total %g\n", total); } - // printout("For element %d ion %d I got mean binding energy of %g (eV)\n", element, ion, 1./total/EV); - return total; } @@ -1227,7 +1231,7 @@ static auto get_oneoverw(const int element, const int ion, const int modelgridin // We are going to start by taking all the high energy limits and ignoring Lelec, so that the // denominator is extremely simplified. Need to get the mean Z value. - double Zbar = 0.0; // mass-weighted average atomic number + double Zbar = 0.; // mass-weighted average atomic number for (int ielement = 0; ielement < get_nelements(); ielement++) { Zbar += grid::get_elem_abundance(modelgridindex, ielement) * get_atomicnumber(ielement); } @@ -1235,14 +1239,14 @@ static auto get_oneoverw(const int element, const int ion, const int modelgridin const double Aconst = 1.33e-14 * EV * EV; const double binding = get_mean_binding_energy(element, ion); - const double oneoverW = Aconst * binding / Zbar / (2 * 3.14159 * pow(QE, 4)); + const double oneoverW = Aconst * binding / Zbar / (2 * PI * pow(QE, 4)); // printout("For element %d ion %d I got W of %g (eV)\n", element, ion, 1./oneoverW/EV); return oneoverW; } static auto calculate_nt_frac_ionization_shell(const int modelgridindex, const int element, const int ion, - const struct collionrow &collionrow) -> double + const collionrow &collionrow) -> double // the fraction of deposition energy that goes into ionising electrons in this particular shell { const double nnion = get_nnion(modelgridindex, element, ion); // hopefully ions per cm^3? @@ -1346,16 +1350,14 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int // (eta_ion / ionpot_ion) = (eta_shell_a / ionpot_shell_a) + (eta_shell_b / ionpot_shell_b) + ... // where eta is the fraction of the deposition energy going into ionization of the ion or shell - double eta_nauger_ionize_over_ionpot_sum[NT_MAX_AUGER_ELECTRONS + 1]; - double eta_nauger_ionize_sum[NT_MAX_AUGER_ELECTRONS + 1]; + std::array eta_nauger_ionize_over_ionpot_sum{}; + std::array eta_nauger_ionize_sum{}; - for (int a = 0; a <= NT_MAX_AUGER_ELECTRONS; a++) { - eta_nauger_ionize_over_ionpot_sum[a] = 0.; - nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = 0.; + std::fill_n(&nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1)], + NT_MAX_AUGER_ELECTRONS + 1, 0.); - eta_nauger_ionize_sum[a] = 0.; - nt_solution[modelgridindex].ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = 0.; - } + std::fill_n(&nt_solution[modelgridindex].ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1)], + NT_MAX_AUGER_ELECTRONS + 1, 0.); double eta_over_ionpot_sum = 0.; double eta_sum = 0.; @@ -1380,7 +1382,7 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int eta_over_ionpot_sum += eta_over_ionpot; - for (int a = 0; a <= NT_MAX_AUGER_ELECTRONS; a++) { + for (size_t a = 0; a < eta_nauger_ionize_sum.size(); a++) { eta_nauger_ionize_over_ionpot_sum[a] += eta_over_ionpot * collionrow.prob_num_auger[a]; eta_nauger_ionize_sum[a] += frac_ionization_shell * collionrow.prob_num_auger[a]; } @@ -1389,48 +1391,32 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int if (NT_MAX_AUGER_ELECTRONS > 0 && matching_nlsubshell_count > 0) { const int nions = get_nions(element); - if (ion < nions - 1) // don't try to ionise the top ion + const int topion = nions - 1; + if (ion < topion) // don't try to ionise the top ion { for (int a = 0; a <= NT_MAX_AUGER_ELECTRONS; a++) { - // printout("test2 Z=%d ion %d a %d probability %g\n", get_atomicnumber(element), get_ionstage(element, ion), a, - // eta_nauger_ionize_over_ionpot_sum[a] / eta_over_ionpot_sum); - if (ion + 1 + a < nions) // not too many Auger electrons to exceed the top ion of this element + const int upperion = ion + 1 + a; + if (upperion <= topion) // not too many Auger electrons to exceed the top ion of this element { nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = eta_nauger_ionize_over_ionpot_sum[a] / eta_over_ionpot_sum; nt_solution[modelgridindex].ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = eta_nauger_ionize_sum[a] / eta_sum; } else { - // the following ensures that multiple ionisations can't send you to an ion stage that is not in the model - // could send it to the top one with a = nions - 1 - ion - 1 + // the following ensures that multiple ionisations can't send you to an ion stage that is not in + // the model. Send it to the highest ion stage instead + const int a_replace = topion - ion - 1; - nt_solution[modelgridindex] - .prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + nions - 1 - ion - 1] += + nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a_replace] += eta_nauger_ionize_over_ionpot_sum[a] / eta_over_ionpot_sum; - nt_solution[modelgridindex] - .ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + nions - 1 - ion - 1] += + nt_solution[modelgridindex].ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a_replace] += eta_nauger_ionize_sum[a] / eta_sum; - // printout("test2b going to Z=%d ion %d a %d with new probability %g\n", get_atomicnumber(element), - // get_ionstage(element, ion), nions - 1 - ion - 1, - // nt_solution[modelgridindex].prob_num_auger[element][ion][nions - 1 - ion - 1]); - nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = 0; nt_solution[modelgridindex].ionenfrac_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = 0.; } } } - - // the following ensures that multiple ionisations can't send you to an ion stage that is not in the model - // for (int a = NT_MAX_AUGER_ELECTRONS; a > 0; a--) - // { - // if ((ion + a + 1) >= nions) - // { - // nt_solution[modelgridindex].prob_num_auger[element][ion][a - 1] += - // nt_solution[modelgridindex].prob_num_auger[element][ion][a]; - // nt_solution[modelgridindex].prob_num_auger[element][ion][a] = 0.; - // } - // } } else { const int a = 0; nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a] = 1.; @@ -1444,11 +1430,11 @@ static void calculate_eff_ionpot_auger_rates(const int modelgridindex, const int } nt_solution[modelgridindex].eff_ionpot[get_uniqueionindex(element, ion)] = eff_ionpot; } else { + printout("WARNING! No matching subshells in NT impact ionisation cross section data for Z=%d ionstage %d.\n", + get_atomicnumber(element), get_ionstage(element, ion)); printout( - "WARNING! No matching subshells in NT impact ionisation cross section data for Z=%d ionstage %d.\n -> " - "Defaulting to work function approximation and ionisation energy is not accounted for in Spencer-Fano " - "solution.\n", - get_atomicnumber(element), get_ionstage(element, ion)); + "-> Defaulting to work function approximation and ionisation energy is not accounted for in Spencer-Fano " + "solution.\n"); nt_solution[modelgridindex].eff_ionpot[get_uniqueionindex(element, ion)] = 1. / get_oneoverw(element, ion, modelgridindex); @@ -1469,7 +1455,7 @@ static auto nt_ionization_ratecoeff_sf(const int modelgridindex, const int eleme { if (grid::get_numassociatedcells(modelgridindex) <= 0) { printout("ERROR: nt_ionization_ratecoeff_sf called on empty cell %d\n", modelgridindex); - abort(); + std::abort(); } const double deposition_rate_density = get_deposition_rate_density(modelgridindex); @@ -1523,7 +1509,7 @@ auto nt_ionization_upperion_probability(const int modelgridindex, const int elem printout(" a %d prob %g\n", a, nt_solution[modelgridindex].prob_num_auger[uniqueionindex * (NT_MAX_AUGER_ELECTRONS + 1) + a]); } - abort(); + std::abort(); } } return prob_remaining; @@ -1588,7 +1574,7 @@ auto nt_ionization_ratecoeff(const int modelgridindex, const int element, const // probably because eff_ionpot = 0 because the solver hasn't been run yet, or no impact ionization cross sections // exist const double Y_nt_wfapprox = nt_ionization_ratecoeff_wfapprox(modelgridindex, element, ion); - // printout("Warning: Spencer-Fano solver gives non-finite ionization rate (%g) for element %d ion_stage %d for + // printout("Warning: Spencer-Fano solver gives non-finite ionization rate (%g) for element %d ionstage %d for // cell %d. Using WF approx instead = %g\n", // Y_nt, get_atomicnumber(element), get_ionstage(element, ion), modelgridindex, Y_nt_wfapprox); return Y_nt_wfapprox; @@ -1597,7 +1583,7 @@ auto nt_ionization_ratecoeff(const int modelgridindex, const int element, const const double Y_nt_wfapprox = nt_ionization_ratecoeff_wfapprox(modelgridindex, element, ion); if (Y_nt_wfapprox > 0) { printout( - "Warning: Spencer-Fano solver gives negative or zero ionization rate (%g) for element Z=%d ion_stage %d " + "Warning: Spencer-Fano solver gives negative or zero ionization rate (%g) for element Z=%d ionstage %d " "cell %d. Using WF approx instead = %g\n", Y_nt, get_atomicnumber(element), get_ionstage(element, ion), modelgridindex, Y_nt_wfapprox); } @@ -1616,7 +1602,7 @@ static auto calculate_nt_excitation_ratecoeff_perdeposition(const int modelgridi { if (nt_solution[modelgridindex].yfunc == nullptr) { printout("ERROR: Call to nt_excitation_ratecoeff with no y vector in memory."); - abort(); + std::abort(); } gsl_vector *xs_excitation_vec = gsl_vector_alloc(SFPTS); @@ -1651,7 +1637,7 @@ auto nt_excitation_ratecoeff(const int modelgridindex, const int element, const if (grid::get_numassociatedcells(modelgridindex) <= 0) { printout("ERROR: nt_excitation_ratecoeff called on empty cell %d\n", modelgridindex); - abort(); + std::abort(); } // if the NT spectrum is stored, we can calculate any non-thermal excitation rate, even if @@ -1684,7 +1670,8 @@ static auto ion_ntion_energyrate(int modelgridindex, int element, int lowerion) // returns the energy rate [erg/cm3/s] going toward non-thermal ionisation of 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 auto maxupperion = nt_ionisation_maxupperion(element, lowerion); + for (int upperion = lowerion + 1; upperion <= maxupperion; upperion++) { const double upperionprobfrac = nt_ionization_upperion_probability(modelgridindex, element, lowerion, upperion, false); // for (int lower = 0; lower < get_nlevels(element, lowerion); lower++) @@ -1748,10 +1735,10 @@ static auto select_nt_ionization(int modelgridindex) -> std::tuple { assert_always(false); } -void do_ntlepton(struct packet *pkt_ptr) { - safeadd(nt_energy_deposited, pkt_ptr->e_cmf); +void do_ntlepton(Packet &pkt) { + atomicadd(nt_energy_deposited, pkt.e_cmf); - const int modelgridindex = grid::get_cell_modelgridindex(pkt_ptr->where); + const int modelgridindex = grid::get_cell_modelgridindex(pkt.where); // macroatom should not be activated in thick cells if (NT_ON && NT_SOLVE_SPENCERFANO && grid::modelgrid[modelgridindex].thick != 1) { @@ -1773,16 +1760,12 @@ void do_ntlepton(struct packet *pkt_ptr) { const int upperion = nt_random_upperion(modelgridindex, element, lowerion, true); // const int upperion = lowerion + 1; - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = upperion; - pkt_ptr->mastate.level = 0; - pkt_ptr->mastate.activatingline = -99; - pkt_ptr->type = TYPE_MA; + pkt.type = TYPE_MA; stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_NTCOLLION); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 20; - pkt_ptr->trueemissiontype = EMTYPE_NOTSET; - pkt_ptr->trueemissionvelocity = -1; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 20; + pkt.trueemissiontype = EMTYPE_NOTSET; + pkt.trueemissionvelocity = -1; stats::increment(stats::COUNTER_NT_STAT_TO_IONIZATION); @@ -1790,15 +1773,15 @@ void do_ntlepton(struct packet *pkt_ptr) { assert_always(upperion < get_nions(element)); assert_always(lowerion >= 0); const double epsilon_trans = epsilon(element, upperion, 0) - epsilon(element, lowerion, 0); - stats::increment_ion_stats(modelgridindex, element, lowerion, stats::ION_NTION, pkt_ptr->e_cmf / epsilon_trans); + stats::increment_ion_stats(modelgridindex, element, lowerion, stats::ION_NTION, pkt.e_cmf / epsilon_trans); stats::increment_ion_stats(modelgridindex, element, upperion, stats::ION_MACROATOM_ENERGYIN_NTCOLLION, - pkt_ptr->e_cmf); + pkt.e_cmf); } // printout("NTLEPTON packet in cell %d selected ionization of Z=%d ionstage %d to %d\n", // modelgridindex, get_atomicnumber(element), get_ionstage(element, lowerion), get_ionstage(element, // upperion)); - + do_macroatom(pkt, {element, upperion, 0, -99}); return; } @@ -1808,33 +1791,28 @@ void do_ntlepton(struct packet *pkt_ptr) { zrand -= frac_ionization; // now zrand is between zero and frac_excitation // the selection algorithm is the same as for the ionization transitions - const auto frac_excitations_list_size = nt_solution[modelgridindex].frac_excitations_list.size(); - for (size_t excitationindex = 0; excitationindex < frac_excitations_list_size; excitationindex++) { - const double frac_deposition_exc = - nt_solution[modelgridindex].frac_excitations_list[excitationindex].frac_deposition; + for (const auto &ntexcitation : nt_solution[modelgridindex].frac_excitations_list) { + const double frac_deposition_exc = ntexcitation.frac_deposition; if (zrand < frac_deposition_exc) { - const int lineindex = nt_solution[modelgridindex].frac_excitations_list[excitationindex].lineindex; + const int lineindex = ntexcitation.lineindex; const int element = globals::linelist[lineindex].elementindex; const int ion = globals::linelist[lineindex].ionindex; // const int lower = linelist[lineindex].lowerlevelindex; const int upper = globals::linelist[lineindex].upperlevelindex; - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion; - pkt_ptr->mastate.level = upper; - pkt_ptr->mastate.activatingline = -99; - pkt_ptr->type = TYPE_MA; + pkt.type = TYPE_MA; stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_NTCOLLEXC); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 21; - pkt_ptr->trueemissiontype = EMTYPE_NOTSET; - pkt_ptr->trueemissionvelocity = -1; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 21; + pkt.trueemissiontype = EMTYPE_NOTSET; + pkt.trueemissionvelocity = -1; stats::increment(stats::COUNTER_NT_STAT_TO_EXCITATION); // printout("NTLEPTON packet selected in cell %d excitation of Z=%d ionstage %d level %d upperlevel %d\n", // modelgridindex, get_atomicnumber(element), get_ionstage(element, ion), lower, upper); + do_macroatom(pkt, {element, ion, upper, -99}); return; } zrand -= frac_deposition_exc; @@ -1844,8 +1822,8 @@ void do_ntlepton(struct packet *pkt_ptr) { } } - pkt_ptr->last_event = 22; - pkt_ptr->type = TYPE_KPKT; + pkt.last_event = 22; + pkt.type = TYPE_KPKT; stats::increment(stats::COUNTER_NT_STAT_TO_KPKT); } @@ -1875,7 +1853,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co double frac_ionization_ion = 0.; double frac_excitation_ion = 0.; - printout(" Z=%d ion_stage %d:\n", Z, ionstage); + printout(" Z=%d ionstage %d:\n", Z, ionstage); // printout(" nnion: %g\n", nnion); printout(" nnion/nntot: %g\n", nnion / nntot); @@ -2018,7 +1996,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co if constexpr (NT_EXCITATION_ON && (MAX_NT_EXCITATIONS_STORED > 0)) { // sort by descending frac_deposition - std::sort(nt_solution[modelgridindex].frac_excitations_list.begin(), + std::sort(EXEC_PAR_UNSEQ 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); }); @@ -2027,7 +2005,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co if (nt_solution[modelgridindex].frac_excitations_list.size() > MAX_NT_EXCITATIONS_STORED) { // truncate the sorted list to save memory - printout(" Truncating non-thermal excitation list from %d to %d transitions.\n", + printout(" Truncating non-thermal excitation list from %zu to %d transitions.\n", nt_solution[modelgridindex].frac_excitations_list.size(), MAX_NT_EXCITATIONS_STORED); nt_solution[modelgridindex].frac_excitations_list.resize(MAX_NT_EXCITATIONS_STORED); } @@ -2037,15 +2015,16 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co sizeof(nt_solution[modelgridindex].frac_excitations_list[0]) / 1024. / 1024.); const auto T_e = grid::get_Te(modelgridindex); - printout(" Top non-thermal excitation fractions (total excitations = %d):\n", + printout(" Top non-thermal excitation fractions (total excitations = %zu):\n", nt_solution[modelgridindex].frac_excitations_list.size()); - int ntransdisplayed = std::min(50, static_cast(nt_solution[modelgridindex].frac_excitations_list.size())); + const int ntransdisplayed = + std::min(50, static_cast(nt_solution[modelgridindex].frac_excitations_list.size())); for (excitationindex = 0; excitationindex < ntransdisplayed; excitationindex++) { const double frac_deposition = nt_solution[modelgridindex].frac_excitations_list[excitationindex].frac_deposition; if (frac_deposition > 0.) { const int lineindex = nt_solution[modelgridindex].frac_excitations_list[excitationindex].lineindex; - const struct linelist_entry *line = &globals::linelist[lineindex]; + const TransitionLine *line = &globals::linelist[lineindex]; const int element = line->elementindex; const int ion = line->ionindex; const int lower = line->lowerlevelindex; @@ -2068,7 +2047,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co const auto coll_str = globals::elements[element].ions[ion].levels[lower].uptrans[uptransindex].coll_str; printout( - " frac_deposition %.3e Z=%d ionstage %d lower %4d upper %4d rad_exc %.1e coll_exc %.1e nt_exc %.1e " + " frac_deposition %.3e Z=%2d ionstage %d lower %4d upper %4d rad_exc %.1e coll_exc %.1e nt_exc %.1e " "nt/tot %.1e collstr %.1e lineindex %d\n", frac_deposition, get_atomicnumber(element), get_ionstage(element, ion), lower, upper, radexc_ratecoeff, collexc_ratecoeff, ntcollexc_ratecoeff, ntcollexc_ratecoeff / exc_ratecoeff, coll_str, lineindex); @@ -2076,7 +2055,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co } // sort the excitation list by ascending lineindex for fast lookup with a binary search - std::sort(nt_solution[modelgridindex].frac_excitations_list.begin(), + std::sort(EXEC_PAR_UNSEQ 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); }); @@ -2086,7 +2065,7 @@ static void analyse_sf_solution(const int modelgridindex, const int timestep, co const double deposition_rate_density_ev = get_deposition_rate_density(modelgridindex) / EV; const double yscalefactor = deposition_rate_density_ev / E_init_ev; - double nne_nt_max = 0.0; + double nne_nt_max = 0.; for (int i = 0; i < SFPTS; i++) { const double endash = gsl_vector_get(envec, i); const double delta_endash = DELTA_E; @@ -2195,7 +2174,7 @@ static void sfmatrix_add_ionization(gsl_matrix *const sfmatrix, const int Z, con assert_always(ionpot_ev >= SF_EMIN); - // printout("Z=%2d ion_stage %d n %d l %d ionpot %g eV\n", + // printout("Z=%2d ionstage %d n %d l %d ionpot %g eV\n", // Z, ionstage, colliondata[n].n, colliondata[n].l, ionpot_ev); const int xsstartindex = get_xs_ionization_vector(vec_xs_ionization, collionrow); @@ -2455,7 +2434,7 @@ void solve_spencerfano(const int modelgridindex, const int timestep, const int i *gsl_matrix_ptr(sfmatrix, i, i) += electron_loss_rate(en * EV, nne) / EV; - double source_integral_to_SF_EMAX = NAN; + double source_integral_to_SF_EMAX{NAN}; if (i < SFPTS - 1) { gsl_vector_const_view source_e_to_SF_EMAX = gsl_vector_const_subvector(sourcevec, i + 1, SFPTS - i - 1); source_integral_to_SF_EMAX = gsl_blas_dasum(&source_e_to_SF_EMAX.vector) * DELTA_E; @@ -2482,7 +2461,7 @@ void solve_spencerfano(const int modelgridindex, const int timestep, const int i const int ionstage = get_ionstage(element, ion); if (first_included_ion_of_element) { - printout(" including Z=%2d ion_stages: ", Z); + printout(" including Z=%2d ionstages: ", Z); for (int i = 1; i < get_ionstage(element, ion); i++) { printout(" "); } @@ -2578,12 +2557,9 @@ void write_restart_data(FILE *gridsave_file) { // write NT excitations fprintf(gridsave_file, "%d\n", static_cast(nt_solution[modelgridindex].frac_excitations_list.size())); - const auto frac_excitations_list_size = nt_solution[modelgridindex].frac_excitations_list.size(); - for (size_t excitationindex = 0; excitationindex < frac_excitations_list_size; excitationindex++) { - fprintf(gridsave_file, "%la %la %d\n", - nt_solution[modelgridindex].frac_excitations_list[excitationindex].frac_deposition, - nt_solution[modelgridindex].frac_excitations_list[excitationindex].ratecoeffperdeposition, - nt_solution[modelgridindex].frac_excitations_list[excitationindex].lineindex); + for (const auto &excitation : nt_solution[modelgridindex].frac_excitations_list) { + fprintf(gridsave_file, "%la %la %d\n", excitation.frac_deposition, excitation.ratecoeffperdeposition, + excitation.lineindex); } // write non-thermal spectrum @@ -2603,19 +2579,19 @@ void read_restart_data(FILE *gridsave_file) { assert_always(fscanf(gridsave_file, "%d\n", &code_check) == 1); if (code_check != 24724518) { printout("ERROR: Beginning of non-thermal restart data not found! Found %d instead of 24724518\n", code_check); - abort(); + std::abort(); } int sfpts_in = 0; - double SF_EMIN_in = NAN; - double SF_EMAX_in = NAN; + double SF_EMIN_in{NAN}; + double SF_EMAX_in{NAN}; assert_always(fscanf(gridsave_file, "%d %la %la\n", &sfpts_in, &SF_EMIN_in, &SF_EMAX_in) == 3); if (sfpts_in != SFPTS || SF_EMIN_in != SF_EMIN || SF_EMAX_in != SF_EMAX) { printout("ERROR: gridsave file specifies %d Spencer-Fano samples, SF_EMIN %lg SF_EMAX %lg\n", sfpts_in, SF_EMIN_in, SF_EMAX_in); printout("ERROR: This simulation has %d Spencer-Fano samples, SF_EMIN %lg SF_EMAX %lg\n", SFPTS, SF_EMIN, SF_EMAX); - abort(); + std::abort(); } for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { @@ -2632,7 +2608,7 @@ void read_restart_data(FILE *gridsave_file) { if (mgi_in != modelgridindex) { printout("ERROR: expected data for cell %d but found cell %d\n", modelgridindex, mgi_in); - abort(); + std::abort(); } for (int uniqueionindex = 0; uniqueionindex < get_includedions(); uniqueionindex++) { @@ -2682,8 +2658,8 @@ void nt_MPI_Bcast(const int modelgridindex, const int root) { return; } - // printout("nonthermal_MPI_Bcast cell %d before: ratecoeff(Z=%d ion_stage %d): %g, eff_ionpot %g eV\n", - // modelgridindex, logged_element_z, logged_ion_stage, + // printout("nonthermal_MPI_Bcast cell %d before: ratecoeff(Z=%d ionstage %d): %g, eff_ionpot %g eV\n", + // modelgridindex, logged_element_z, logged_ionstage, // nt_ionization_ratecoeff_sf(modelgridindex, logged_element_index, logged_ion_index), // get_eff_ionpot(modelgridindex, logged_element_index, logged_ion_index) / EV); diff --git a/nonthermal.h b/nonthermal.h index 18093c485..1bf37faca 100644 --- a/nonthermal.h +++ b/nonthermal.h @@ -1,3 +1,4 @@ +#pragma once #ifndef NONTHERMAL_H #define NONTHERMAL_H @@ -9,17 +10,17 @@ namespace nonthermal { void init(int my_rank, int ndo_nonempty); void close_file(); void solve_spencerfano(int modelgridindex, int timestep, int iteration); -auto nt_ionization_ratecoeff(int modelgridindex, int element, int ion) -> double; -auto nt_ionization_upperion_probability(int modelgridindex, int element, int lowerion, int upperion, - bool energyweighted) -> double; -auto nt_ionisation_maxupperion(int element, int lowerion) -> int; -auto nt_random_upperion(int modelgridindex, int element, int lowerion, bool energyweighted) -> int; +[[nodiscard]] auto nt_ionization_ratecoeff(int modelgridindex, int element, int ion) -> double; +[[nodiscard]] auto nt_ionization_upperion_probability(int modelgridindex, int element, int lowerion, int upperion, + bool energyweighted) -> double; +[[nodiscard]] auto nt_ionisation_maxupperion(int element, int lowerion) -> int; +[[nodiscard]] auto nt_random_upperion(int modelgridindex, int element, int lowerion, bool energyweighted) -> int; void calculate_deposition_rate_density(int modelgridindex, int timestep); -auto get_deposition_rate_density(int modelgridindex) -> double; -auto get_nt_frac_heating(int modelgridindex) -> float; -auto nt_excitation_ratecoeff(int modelgridindex, int element, int ion, int lowerlevel, int uptransindex, - double epsilon_trans, int lineindex) -> double; -void do_ntlepton(struct packet *pkt_ptr); +[[nodiscard]] auto get_deposition_rate_density(int modelgridindex) -> double; +[[nodiscard]] auto get_nt_frac_heating(int modelgridindex) -> float; +[[nodiscard]] auto nt_excitation_ratecoeff(int modelgridindex, int element, int ion, int lowerlevel, int uptransindex, + double epsilon_trans, int lineindex) -> double; +void do_ntlepton(Packet &pkt); void write_restart_data(FILE *gridsave_file); void read_restart_data(FILE *gridsave_file); void nt_MPI_Bcast(int modelgridindex, int root); diff --git a/packet.cc b/packet.cc index 8312e7fd2..a4091ddd1 100644 --- a/packet.cc +++ b/packet.cc @@ -1,30 +1,40 @@ #include "packet.h" +#ifdef MPI_ON +#include +#endif + #include #include +#include +#include +#include #include #include -#include +#include +#include #include #include #include +#include "artisoptions.h" +#include "constants.h" #include "decay.h" +#include "globals.h" #include "grid.h" #include "input.h" #include "sn3d.h" #include "vectors.h" -static void place_pellet(const double e0, const int cellindex, const int pktnumber, struct packet *pkt_ptr) +static void place_pellet(const double e0, const int cellindex, const int pktnumber, Packet &pkt) /// This subroutine places pellet n with energy e0 in cell m { /// First choose a position for the pellet. In the cell. - /// n is the index of the packet. m is the index for the grid cell. - pkt_ptr->where = cellindex; - pkt_ptr->number = pktnumber; /// record the packets number for debugging - pkt_ptr->prop_time = globals::tmin; - // pkt_ptr->last_cross = BOUNDARY_NONE; - pkt_ptr->originated_from_particlenotgamma = false; + pkt.where = cellindex; + pkt.number = pktnumber; /// record the packets number for debugging + pkt.prop_time = globals::tmin; + // pkt.last_cross = BOUNDARY_NONE; + pkt.originated_from_particlenotgamma = false; if constexpr (GRID_TYPE == GRID_SPHERICAL1D) { const double zrand = rng_uniform(); @@ -35,50 +45,47 @@ static void place_pellet(const double e0, const int cellindex, const int pktnumb // assert_always(radius >= r_inner); // assert_always(radius <= r_outer); - get_rand_isotropic_unitvec(pkt_ptr->pos); - vec_scale(pkt_ptr->pos, radius); + pkt.pos = vec_scale(get_rand_isotropic_unitvec(), radius); } else if constexpr (GRID_TYPE == GRID_CYLINDRICAL2D) { - const double zrand1 = rng_uniform(); + const double zrand = rng_uniform_pos(); 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 rcyl_rand = std::sqrt(zrand * std::pow(rcyl_inner, 2) + (1. - zrand) * std::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; + pkt.pos[0] = std::cos(theta_rand) * rcyl_rand; + pkt.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)); + pkt.pos[2] = grid::get_cellcoordmin(cellindex, 1) + (rng_uniform_pos() * 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(cellindex, axis)); + pkt.pos[axis] = grid::get_cellcoordmin(cellindex, axis) + (rng_uniform_pos() * 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); + assert_always(grid::get_cellindex_from_pos(pkt.pos, pkt.prop_time) == cellindex); const int mgi = grid::get_cell_modelgridindex(cellindex); - decay::setup_radioactive_pellet(e0, mgi, pkt_ptr); + decay::setup_radioactive_pellet(e0, mgi, pkt); // initial e_rf is probably never needed (e_rf is set at pellet decay time), but we // might as well give it a correct value since this code is fast and runs only once // 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->pos, pkt_ptr->dir, pkt_ptr->prop_time); - pkt_ptr->e_rf = pkt_ptr->e_cmf / dopplerfactor; + pkt.dir = vec_norm(pkt.pos); // assign dir = pos / vec_len(pos) + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.e_rf = pkt.e_cmf / dopplerfactor; - pkt_ptr->trueemissiontype = EMTYPE_NOTSET; + pkt.trueemissiontype = EMTYPE_NOTSET; } -void packet_init(struct packet *pkt) +void packet_init(Packet *pkt) /// Subroutine that initialises the packets if we start a new simulation. { #ifdef MPI_ON @@ -99,13 +106,12 @@ void packet_init(struct packet *pkt) decay::setup_decaypath_energy_per_mass(); - // Need to get a normalisation factor. + // Need to get a normalisation factor auto en_cumulative = std::vector(grid::ngrid); - double norm = 0.0; + double norm = 0.; for (int m = 0; m < grid::ngrid; m++) { - const int mgi = grid::get_cell_modelgridindex(m); - if (mgi < grid::get_npts_model()) // some grid cells are empty + if (const int mgi = grid::get_cell_modelgridindex(m); mgi < grid::get_npts_model()) // some grid cells are empty { double q = decay::get_modelcell_simtime_endecay_per_mass(mgi); if constexpr (INITIAL_PACKETS_ON && USE_MODEL_INITIAL_ENERGY) { @@ -129,27 +135,26 @@ void packet_init(struct packet *pkt) if (globals::npkts > MPKTS) { printout("Too many packets. Abort.\n"); - abort(); + std::abort(); } printout("Placing pellets...\n"); - for (int n = 0; n < globals::npkts; n++) { - const double zrand = rng_uniform(); - const double targetval = zrand * norm; + auto allpkts = std::ranges::iota_view{0, globals::npkts}; + std::for_each(allpkts.begin(), allpkts.end(), [&, norm, e0](const int n) { + const double targetval = rng_uniform() * norm; - // first en_cumulative[i] such that en_cumulative[i] > 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.cbegin(), upperval); + // first i such that en_cumulative[i] > targetval + const int cellindex = std::distance(en_cumulative.cbegin(), + std::upper_bound(en_cumulative.cbegin(), en_cumulative.cend(), targetval)); + assert_always(cellindex < grid::ngrid); - place_pellet(e0, cellindex, n, &pkt[n]); - } + place_pellet(e0, cellindex, n, pkt[n]); + }); decay::free_decaypath_energy_per_mass(); // will no longer be needed after packets are set up double e_cmf_total = 0.; for (int n = 0; n < globals::npkts; n++) { - pkt[n].interactions = 0; e_cmf_total += pkt[n].e_cmf; } const double e_ratio = etot / e_cmf_total; @@ -163,31 +168,30 @@ void packet_init(struct packet *pkt) printout("total energy that will be freed during simulation time: %g erg\n", e_cmf_total); } -void write_packets(char filename[], const struct packet *const pkt) { +void write_packets(const char filename[], const Packet *const pkt) { // write packets text file FILE *packets_file = fopen_required(filename, "w"); fprintf(packets_file, "#number where type_id posx posy posz dirx diry dirz last_cross tdecay e_cmf e_rf nu_cmf nu_rf " - "escape_type_id escape_time next_trans interactions last_event emissiontype trueemissiontype " + "escape_type_id escape_time next_trans last_event emissiontype trueemissiontype " "em_posx em_posy em_posz absorption_type absorption_freq nscatterings em_time absorptiondirx absorptiondiry " "absorptiondirz stokes1 stokes2 stokes3 pol_dirx pol_diry pol_dirz originated_from_positron " "true_emission_velocity trueem_time pellet_nucindex\n"); for (int i = 0; i < globals::npkts; i++) { fprintf(packets_file, "%d ", pkt[i].number); fprintf(packets_file, "%d ", pkt[i].where); - fprintf(packets_file, "%d ", pkt[i].type); + fprintf(packets_file, "%d ", static_cast(pkt[i].type)); fprintf(packets_file, "%lg %lg %lg ", pkt[i].pos[0], pkt[i].pos[1], pkt[i].pos[2]); fprintf(packets_file, "%lg %lg %lg ", pkt[i].dir[0], pkt[i].dir[1], pkt[i].dir[2]); - fprintf(packets_file, "%d ", pkt[i].last_cross); + fprintf(packets_file, "%d ", static_cast(pkt[i].last_cross)); fprintf(packets_file, "%g ", pkt[i].tdecay); fprintf(packets_file, "%g ", pkt[i].e_cmf); fprintf(packets_file, "%g ", pkt[i].e_rf); fprintf(packets_file, "%g ", pkt[i].nu_cmf); fprintf(packets_file, "%g ", pkt[i].nu_rf); - fprintf(packets_file, "%d ", pkt[i].escape_type); + fprintf(packets_file, "%d ", static_cast(pkt[i].escape_type)); fprintf(packets_file, "%g ", pkt[i].escape_time); fprintf(packets_file, "%d ", pkt[i].next_trans); - fprintf(packets_file, "%d ", pkt[i].interactions); fprintf(packets_file, "%d ", pkt[i].last_event); fprintf(packets_file, "%d ", pkt[i].emissiontype); fprintf(packets_file, "%d ", pkt[i].trueemissiontype); @@ -208,20 +212,20 @@ void write_packets(char filename[], const struct packet *const pkt) { fclose(packets_file); } -void read_temp_packetsfile(const int timestep, const int my_rank, struct packet *const pkt) { +void read_temp_packetsfile(const int timestep, const int my_rank, Packet *pkt) { // read packets binary file char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "packets_%.4d_ts%d.tmp", my_rank, timestep); printout("Reading %s...", filename); FILE *packets_file = fopen_required(filename, "rb"); - assert_always(std::fread(pkt, sizeof(struct packet), globals::npkts, packets_file) == (size_t)globals::npkts); + assert_always(std::fread(pkt, sizeof(Packet), globals::npkts, packets_file) == (size_t)globals::npkts); // read_packets(packets_file); fclose(packets_file); printout("done\n"); } -auto verify_temp_packetsfile(const int timestep, const int my_rank, const struct packet *const pkt) -> bool { +auto verify_temp_packetsfile(const int timestep, const int my_rank, const Packet *const pkt) -> bool { // return true if verification is good, otherwise return false // read packets binary file @@ -230,13 +234,13 @@ auto verify_temp_packetsfile(const int timestep, const int my_rank, const struct printout("Verifying file %s...", filename); FILE *packets_file = fopen_required(filename, "rb"); - struct packet pkt_in; + Packet pkt_in; bool readback_passed = true; for (int n = 0; n < globals::npkts; n++) { - assert_always(std::fread(&pkt_in, sizeof(struct packet), 1, packets_file) == 1); + assert_always(std::fread(&pkt_in, sizeof(Packet), 1, packets_file) == 1); if (pkt_in != pkt[n]) { printout("failed on packet %d\n", n); - printout(" compare number %ld %ld\n", pkt_in.number, pkt[n].number); + printout(" compare number %d %d\n", pkt_in.number, pkt[n].number); printout(" compare nu_cmf %lg %lg\n", pkt_in.nu_cmf, pkt[n].nu_cmf); printout(" compare e_rf %lg %lg\n", pkt_in.e_rf, pkt[n].e_rf); readback_passed = false; @@ -251,7 +255,7 @@ auto verify_temp_packetsfile(const int timestep, const int my_rank, const struct return readback_passed; } -void read_packets(const char filename[], struct packet *pkt) { +void read_packets(const char filename[], Packet *pkt) { // read packets*.out text format file std::ifstream packets_file(filename); assert_always(packets_file.is_open()); @@ -272,7 +276,7 @@ void read_packets(const char filename[], struct packet *pkt) { "ERROR: More data found beyond packet %d (expecting %d packets). Recompile exspec with the correct number " "of packets. Run (wc -l < packets00_0000.out) to count them.\n", packets_read, globals::npkts); - abort(); + std::abort(); } std::istringstream ssline(line); @@ -297,8 +301,7 @@ void read_packets(const char filename[], struct packet *pkt) { ssline >> escape_type >> pkt[i].escape_time; pkt[i].escape_type = static_cast(escape_type); - ssline >> pkt[i].next_trans >> pkt[i].interactions >> pkt[i].last_event; - assert_always(pkt[i].interactions >= 0); + ssline >> pkt[i].next_trans >> pkt[i].last_event; ssline >> pkt[i].emissiontype >> pkt[i].trueemissiontype; @@ -330,8 +333,6 @@ void read_packets(const char filename[], struct packet *pkt) { "ERROR: Read failed after packet %d (expecting %d packets). Recompile exspec with the correct number of " "packets. Run (wc -l < packets00_0000.out) to count them.\n", packets_read, globals::npkts); - abort(); + std::abort(); } - - packets_file.close(); } diff --git a/packet.h b/packet.h index 477da1cfe..66686ff57 100644 --- a/packet.h +++ b/packet.h @@ -1,10 +1,13 @@ +#pragma once #ifndef PACKET_H #define PACKET_H +#include +#include #include #include -enum packet_type { +enum packet_type : int { TYPE_ESCAPE = 32, TYPE_RADIOACTIVE_PELLET = 100, TYPE_GAMMA = 10, @@ -16,17 +19,17 @@ enum packet_type { TYPE_PRE_KPKT = 120, }; -constexpr int EMTYPE_NOTSET = -9999000; -constexpr int EMTYPE_FREEFREE = -9999999; +constexpr int EMTYPE_NOTSET{-9999000}; +constexpr int EMTYPE_FREEFREE{-9999999}; -struct mastate { +struct MacroAtomState { int element; /// macro atom of type element (this is an element index) int ion; /// in ionstage ion (this is an ion index) int level; /// and level=level (this is a level index) int activatingline; /// Linelistindex of the activating line for bb activated MAs, -99 else. }; -enum cell_boundary { +enum cell_boundary : int { COORD0_MIN = 101, COORD0_MAX = 102, COORD1_MIN = 103, @@ -36,62 +39,67 @@ enum cell_boundary { BOUNDARY_NONE = 107, }; -struct packet { - 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] = {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. - double nu_rf; // The frequency in the rest frame. - int next_trans; // This keeps track of the next possible line interaction of a rpkt by storing - // 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 = -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 = -1.; // first thermal emission time [s] - double absorptionfreq; // records nu_rf of packet at last absorption - 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 = -1; // index into decay::decaytypes - int pellet_nucindex = -1; // nuclide index of the decaying species - float trueemissionvelocity = -1; - struct mastate mastate; +struct Packet { + enum packet_type type {}; // type of packet (k-, r-, etc.) + double prop_time{-1.}; // internal clock to track how far in time the packet has been propagated + int where{-1}; // The propagation grid cell that the packet is in. + enum cell_boundary last_cross { BOUNDARY_NONE }; // To avoid rounding errors on cell crossing. + int nscatterings{0}; // records number of electron scatterings a r-pkt undergone since it was emitted + int last_event{0}; // debug: stores information about the packets history + std::array pos{}; // Position of the packet (x,y,z). + std::array dir{}; // Direction of propagation. (x,y,z). Always a unit vector. + double e_cmf{0.}; // The energy the packet carries in the co-moving frame. + double e_rf{0.}; // The energy the packet carries in the rest frame. + double nu_cmf{0.}; // The frequency in the co-moving frame. + double nu_rf{0.}; // The frequency in the rest frame. + int next_trans{-1}; // This keeps track of the next possible line interaction of a rpkt by storing + // 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 + std::array em_pos{NAN}; // Position of the last emission (x,y,z). + float em_time{-1.}; + int absorptiontype{0}; // 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{-1.}; // first thermal emission time [s] + double absorptionfreq{}; // records nu_rf of packet at last absorption + std::array absorptiondir{}; // Direction of propagation (x,y,z) when a packet was last absorbed in a line. + // Always a unit vector. + std::array stokes{}; // I, Q and U Stokes parameters + std::array pol_dir{}; // 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 {}; // In which form when 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{false}; // first-non-pellet packet type was gamma + int pellet_decaytype{-1}; // index into decay::decaytypes + int pellet_nucindex{-1}; // nuclide index of the decaying species + float trueemissionvelocity{-1}; - inline auto operator==(const packet &rhs) -> bool { + inline auto operator==(const Packet &rhs) -> bool { return (number == rhs.number && type == rhs.type && (em_pos[0] == rhs.em_pos[0] && em_pos[1] == rhs.em_pos[1] && em_pos[2] == rhs.em_pos[2]) && - nu_cmf == rhs.nu_cmf && where == rhs.where && prop_time == rhs.prop_time && - mastate.activatingline == rhs.mastate.activatingline && tdecay == rhs.tdecay && + nu_cmf == rhs.nu_cmf && where == rhs.where && prop_time == rhs.prop_time && tdecay == rhs.tdecay && pellet_nucindex == rhs.pellet_nucindex); } }; -void packet_init(struct packet *pkt); -void write_packets(char filename[], const struct packet *pkt); -void read_packets(const char filename[], struct packet *pkt); -void read_temp_packetsfile(int timestep, int my_rank, struct packet *pkt); -auto verify_temp_packetsfile(int timestep, int my_rank, const struct packet *pkt) -> bool; +enum last_event_type { + LASTEVENT_KPKT_TO_RPKT_FFBB = 6, + LASTEVENT_KPKT_TO_RPKT_FB = 7, + LASTEVENT_KPKT_TO_MA_COLLEXC = 8, + LASTEVENT_KPKT_TO_MA_COLLION = 9, + LASTEVENT_ELECTRONSCATTERING = 12, +}; + +void packet_init(Packet *pkt); +void write_packets(const char filename[], const Packet *pkt); +void read_packets(const char filename[], Packet *pkt); +void read_temp_packetsfile(int timestep, int my_rank, Packet *pkt); +[[nodiscard]] auto verify_temp_packetsfile(int timestep, int my_rank, const Packet *pkt) -> bool; #endif // PACKET_H diff --git a/radfield.cc b/radfield.cc index 29f89eb90..68eff7103 100644 --- a/radfield.cc +++ b/radfield.cc @@ -1,48 +1,58 @@ #include "radfield.h" +#include + +#ifdef MPI_ON +#include +#endif + #include #include +#include #include #include #include #include -#include +#include +#include #include +#include +#include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "globals.h" #include "grid.h" +#include "ratecoeff.h" +#include "rpkt.h" #include "sn3d.h" -#include "vectors.h" namespace radfield { -static std::vector J_normfactor; +namespace { -// typedef enum -// { -// FIT_DILUTE_BLACKBODY = 0, -// FIT_CONSTANT = 1, -// } enum_bin_fit_type; +std::vector J_normfactor; -struct radfieldbin_solution { +struct RadFieldBinSolution { // these two parameters are used in the current timestep, but were calculated // from the values of J and nuJ in the previous timestep float W; // dilution (scaling) factor float T_R; // radiation temperature - // enum_bin_fit_type fit_type; }; -struct radfieldbin { +struct RadFieldBin { double J_raw; // value needs to be multipled by J_normfactor to get the true value double nuJ_raw; int contribcount; }; -static double radfieldbin_nu_upper[RADFIELDBINCOUNT]; // array of upper frequency boundaries of bins -static struct radfieldbin *radfieldbins = nullptr; -static struct radfieldbin_solution *radfieldbin_solutions = nullptr; +constexpr double radfieldbins_delta_nu = + (nu_upper_last_initial - nu_lower_first_initial) / (RADFIELDBINCOUNT - 1); // - 1 for the top super bin + +struct RadFieldBin *radfieldbins{}; +struct RadFieldBinSolution *radfieldbin_solutions{}; #ifdef MPI_ON MPI_Win win_radfieldbin_solutions = MPI_WIN_NULL; @@ -58,135 +68,111 @@ struct Jb_lu_estimator { // reallocate the detailed line arrays in units of BLOCKSIZEJBLUE constexpr int BLOCKSIZEJBLUE = 128; -static int detailed_linecount = 0; +int detailed_linecount = 0; // array of indicies into the linelist[] array for selected lines -static int *detailed_lineindicies; +int *detailed_lineindicies; -static struct Jb_lu_estimator **prev_Jb_lu_normed = nullptr; // value from the previous timestep -static struct Jb_lu_estimator **Jb_lu_raw = nullptr; // unnormalised estimator for the current timestep +struct Jb_lu_estimator **prev_Jb_lu_normed{}; // value from the previous timestep +struct Jb_lu_estimator **Jb_lu_raw{}; // unnormalised estimator for the current timestep // ** end detailed lines -static float *prev_bfrate_normed = nullptr; // values from the previous timestep -static double *bfrate_raw = nullptr; // unnormalised estimators for the current timestep +float *prev_bfrate_normed{}; // values from the previous timestep +std::vector bfrate_raw; // unnormalised estimators for the current timestep // expensive debugging mode to track the contributions to each bound-free rate estimator -static std::vector J; // after normalisation: [ergs/s/sr/cm2/Hz] +std::vector J; // after normalisation: [ergs/s/sr/cm2/Hz] #ifdef DO_TITER -static std::vector J_reduced_save; +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 std::vector nuJ; +std::vector nuJ; #ifdef DO_TITER -static std::vector nuJ_reduced_save; +std::vector nuJ_reduced_save; #endif -using enum_prefactor = enum { - ONE = 0, - TIMES_NU = 1, -}; - -using gsl_planck_integral_paras = struct { +struct gsl_planck_integral_paras { double T_R; - enum_prefactor prefactor; + bool times_nu; }; -using gsl_T_R_solver_paras = struct { +struct gsl_T_R_solver_paras { int modelgridindex; int binindex; }; -static FILE *radfieldfile = nullptr; - -static inline auto get_bin_nu_upper(int binindex) -> double { return radfieldbin_nu_upper[binindex]; } - -static void setup_bin_boundaries() { - // double prev_nu_upper = nu_lower_first_initial; - - // choose between equally spaced in energy/frequency or wavelength (before bf edges shift boundaries around) - const double delta_nu = - (nu_upper_last_initial - nu_lower_first_initial) / (RADFIELDBINCOUNT - 1); // - 1 for the top super bin - // const double lambda_lower_first_initial = 1e8 * CLIGHT / nu_lower_first_initial; - // const double lambda_upper_last_initial = 1e8 * CLIGHT / nu_upper_last_initial; - // const double delta_lambda = (lambda_upper_last_initial - lambda_lower_first_initial) / RADFIELDBINCOUNT; - - for (int binindex = 0; binindex < RADFIELDBINCOUNT - 1; binindex++) { - // radfieldbin_nu_upper[binindex] = 1e8 * CLIGHT / (lambda_lower_first_initial + (binindex + 1) * delta_lambda); - radfieldbin_nu_upper[binindex] = nu_lower_first_initial + (binindex + 1) * delta_nu; - - // Align the bin edges with bound-free edges, except for the last one - // if (binindex < RADFIELDBINCOUNT - 1) - // { - // for (int i = 0; i < globals::nbfcontinua_ground; i++) - // { - // const double nu_edge = globals::groundcont[i].nu_edge; - // const double eV_edge = H * nu_edge / EV; - // const double angstrom_edge = 1e8 * CLIGHT / nu_edge; - // const int element = globals::groundcont[i].element; - // const int ion = globals::groundcont[i].ion; - // const int level = globals::groundcont[i].level; - // const int phixstargetindex = globals::groundcont[i].phixstargetindex; - // - // const int Z = get_atomicnumber(element); - // const int ion_stage = get_ionstage(element, ion); - // const int upperionlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); - // - // //printout("bf edge at %g, nu_lower_first %g, nu_upper_last %g\n",nu_edge,nu_lower_first,nu_upper_last); - // - // // this is compares the highest and lowest bins to the bound-free list, only do it once, i.e. binindex == 0 - // if (binindex == 0 && ((nu_edge < nu_lower_first_initial) || (nu_edge > nu_upper_last_initial))) - // { - // printout("Missed bf edge at %12.5e Hz (%6.2f eV, %6.1f A), nu_lower_first %11.5e Hz, nu_upper_last %11.5e - // Hz, Z=%d ion_stage %d level %d upperionlevel %d\n", - // nu_edge, eV_edge, angstrom_edge, nu_lower_first_initial, nu_upper_last_initial, Z, ion_stage, - // level, upperionlevel); - // } - // - // const double bin_nu_upper = get_bin_nu_upper(binindex); - // if ((nu_edge > prev_nu_upper) && (nu_edge < bin_nu_upper)) - // { - // printout("Shifting bin %d nu_upper from %12.5e Hz to bf edge at %12.5e Hz (%6.2f eV, %6.1f A) for Z=%d - // ion_stage %d level %d upperionlevel %d\n", - // binindex, bin_nu_upper, nu_edge, eV_edge, angstrom_edge, Z, ion_stage, level, upperionlevel); - // radfieldbin_nu_upper[binindex] = nu_edge; - // } - // } - // } - // prev_nu_upper = get_bin_nu_upper(binindex); - } - radfieldbin_nu_upper[RADFIELDBINCOUNT - 1] = nu_upper_superbin; // very top end super bin +FILE *radfieldfile{}; + +constexpr auto get_bin_nu_upper(int binindex) -> double { + assert_testmodeonly(binindex < RADFIELDBINCOUNT); + if (binindex == RADFIELDBINCOUNT - 1) { + return nu_upper_superbin; + } + return nu_lower_first_initial + (binindex + 1) * radfieldbins_delta_nu; +} + +constexpr auto get_bin_nu_lower(int binindex) -> double { + if (binindex > 0) { + return get_bin_nu_upper(binindex - 1); + } + return nu_lower_first_initial; +} + +constexpr auto select_bin(const double nu) -> int { + // find the left-closed bin [nu_lower, nu_upper) that nu belongs to + + if (nu < nu_lower_first_initial) { + return -2; // out of range, nu lower than lowest bin's lower boundary + } + if (nu >= nu_upper_superbin) { + // out of range, nu higher than highest bin's upper boundary + return -1; + } + if (nu >= nu_upper_last_initial) { + // in the superbin. separate case because the delta_nu is different to the other bins + return RADFIELDBINCOUNT - 1; + } + + const int binindex = static_cast((nu - nu_lower_first_initial) / radfieldbins_delta_nu); + + if (nu == get_bin_nu_upper(binindex)) { + // exactly on the upper boundary of the bin, so add 1 to ensure we get the left-closed bin + return binindex + 1; + } + + return binindex; } -static void realloc_detailed_lines(const int new_size) { +void realloc_detailed_lines(const int new_size) { auto *newptr = static_cast(realloc(detailed_lineindicies, new_size * sizeof(int))); if (newptr == nullptr) { printout("ERROR: Not enough memory to reallocate detailed Jblue estimator line list\n"); - abort(); + std::abort(); } assert_always(newptr != nullptr); detailed_lineindicies = newptr; for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { if (grid::get_numassociatedcells(modelgridindex) > 0) { - prev_Jb_lu_normed[modelgridindex] = static_cast( - realloc(prev_Jb_lu_normed[modelgridindex], new_size * sizeof(struct Jb_lu_estimator))); + prev_Jb_lu_normed[modelgridindex] = static_cast( + realloc(prev_Jb_lu_normed[modelgridindex], new_size * sizeof(Jb_lu_estimator))); - Jb_lu_raw[modelgridindex] = static_cast( - realloc(Jb_lu_raw[modelgridindex], new_size * sizeof(struct Jb_lu_estimator))); + Jb_lu_raw[modelgridindex] = + static_cast(realloc(Jb_lu_raw[modelgridindex], new_size * sizeof(Jb_lu_estimator))); if (prev_Jb_lu_normed[modelgridindex] == nullptr || Jb_lu_raw[modelgridindex] == nullptr) { printout("ERROR: Not enough memory to reallocate detailed Jblue estimator list for cell %d.\n", modelgridindex); - abort(); + std::abort(); } } } } -static void add_detailed_line(const int lineindex) +void add_detailed_line(const int lineindex) // associate a Jb_lu estimator with a particular lineindex to be used // instead of the general radiation field model { @@ -210,10 +196,55 @@ static void add_detailed_line(const int lineindex) // printout("Added Jblue estimator for lineindex %d count %d\n", lineindex, detailed_linecount); } +auto get_bin_J(int modelgridindex, int binindex) -> double +// get the normalised J_nu +{ + const ptrdiff_t 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); + return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].J_raw * J_normfactor[nonemptymgi]; +} + +auto get_bin_nuJ(int modelgridindex, int binindex) -> double { + const ptrdiff_t 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); + return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].nuJ_raw * J_normfactor[nonemptymgi]; +} + +auto get_bin_nu_bar(int modelgridindex, int binindex) -> double +// importantly, this is average beween the current and previous timestep +{ + const double nuJ_sum = get_bin_nuJ(modelgridindex, binindex); + const double J_sum = get_bin_J(modelgridindex, binindex); + return nuJ_sum / J_sum; +} + +auto get_bin_contribcount(int modelgridindex, int binindex) -> int { + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].contribcount; +} + +auto get_bin_W(int modelgridindex, int binindex) -> float { + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + return radfieldbin_solutions[nonemptymgi * RADFIELDBINCOUNT + binindex].W; +} + +auto get_bin_T_R(int modelgridindex, int binindex) -> float { + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + return radfieldbin_solutions[nonemptymgi * RADFIELDBINCOUNT + binindex].T_R; +} + +} // anonymous namespace + void init(int my_rank, int ndo_nonempty) // this should be called only after the atomic data is in memory { - const int nonempty_npts_model = grid::get_nonempty_npts_model(); + const ptrdiff_t nonempty_npts_model = grid::get_nonempty_npts_model(); J_normfactor.resize(nonempty_npts_model + 1); J.resize(nonempty_npts_model + 1); @@ -229,10 +260,8 @@ void init(int my_rank, int ndo_nonempty) nuJ.resize(nonempty_npts_model + 1); #endif - prev_Jb_lu_normed = - static_cast(malloc((grid::get_npts_model() + 1) * sizeof(struct Jb_lu_estimator *))); - Jb_lu_raw = - static_cast(malloc((grid::get_npts_model() + 1) * sizeof(struct Jb_lu_estimator *))); + prev_Jb_lu_normed = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(Jb_lu_estimator *))); + Jb_lu_raw = static_cast(malloc((grid::get_npts_model() + 1) * sizeof(Jb_lu_estimator *))); detailed_linecount = 0; @@ -305,18 +334,14 @@ void init(int my_rank, int ndo_nonempty) snprintf(filename, MAXFILENAMELENGTH, "radfield_%.4d.out", my_rank); assert_always(radfieldfile == nullptr); radfieldfile = fopen_required(filename, "w"); - fprintf(radfieldfile, "%8s %15s %8s %11s %11s %9s %9s %9s %9s %9s %12s\n", "timestep", "modelgridindex", - "bin_num", "nu_lower", "nu_upper", "nuJ", "J", "J_nu_avg", "ncontrib", "T_R", "W"); + fprintf(radfieldfile, "timestep modelgridindex bin_num nu_lower nu_upper nuJ J J_nu_avg ncontrib T_R W\n"); fflush(radfieldfile); } - setup_bin_boundaries(); - - const size_t mem_usage_bins = nonempty_npts_model * RADFIELDBINCOUNT * sizeof(struct radfieldbin); - radfieldbins = - static_cast(malloc(nonempty_npts_model * RADFIELDBINCOUNT * sizeof(struct radfieldbin))); + const size_t mem_usage_bins = nonempty_npts_model * RADFIELDBINCOUNT * sizeof(RadFieldBin); + radfieldbins = static_cast(malloc(nonempty_npts_model * RADFIELDBINCOUNT * sizeof(RadFieldBin))); - const size_t mem_usage_bin_solutions = nonempty_npts_model * RADFIELDBINCOUNT * sizeof(struct radfieldbin_solution); + const size_t mem_usage_bin_solutions = nonempty_npts_model * RADFIELDBINCOUNT * sizeof(RadFieldBinSolution); #ifdef MPI_ON { @@ -325,8 +350,8 @@ void init(int my_rank, int ndo_nonempty) if (globals::rank_in_node == 0) { my_rank_cells += nonempty_npts_model - (my_rank_cells * globals::node_nprocs); } - auto size = static_cast(my_rank_cells * RADFIELDBINCOUNT * sizeof(struct radfieldbin_solution)); - int disp_unit = sizeof(struct radfieldbin_solution); + auto size = static_cast(my_rank_cells * RADFIELDBINCOUNT * sizeof(RadFieldBinSolution)); + int disp_unit = sizeof(RadFieldBinSolution); MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, &radfieldbin_solutions, &win_radfieldbin_solutions); @@ -334,8 +359,8 @@ void init(int my_rank, int ndo_nonempty) } #else { - radfieldbin_solutions = static_cast( - malloc(nonempty_npts_model * RADFIELDBINCOUNT * sizeof(struct radfieldbin_solution))); + radfieldbin_solutions = static_cast( + malloc(nonempty_npts_model * RADFIELDBINCOUNT * sizeof(RadFieldBinSolution))); } #endif @@ -370,27 +395,30 @@ void init(int my_rank, int ndo_nonempty) printout("[info] mem_usage: detailed bf estimators for non-empty cells occupy %.3f MB (node shared memory)\n", nonempty_npts_model * globals::bfestimcount * sizeof(float) / 1024. / 1024.); - bfrate_raw = static_cast(malloc(nonempty_npts_model * globals::bfestimcount * sizeof(double))); + bfrate_raw.resize(nonempty_npts_model * globals::bfestimcount); printout("[info] mem_usage: detailed bf estimator acculumators for non-empty cells occupy %.3f MB\n", nonempty_npts_model * globals::bfestimcount * sizeof(double) / 1024. / 1024.); } - for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { - if (grid::get_numassociatedcells(modelgridindex) > 0) { - zero_estimators(modelgridindex); + zero_estimators(); - if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - if (globals::rank_in_node == 0) { - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; - radfieldbin_solutions[mgibinindex].W = -1.; - radfieldbin_solutions[mgibinindex].T_R = -1.; - } + if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { +#ifdef MPI_ON + MPI_Barrier(globals::mpi_comm_node); +#endif + if (globals::rank_in_node == 0) { + for (ptrdiff_t nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { + for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { + const auto mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + radfieldbin_solutions[mgibinindex].W = -1.; + radfieldbin_solutions[mgibinindex].T_R = -1.; } } } +#ifdef MPI_ON + MPI_Barrier(globals::mpi_comm_node); +#endif } } @@ -398,22 +426,20 @@ void init(int my_rank, int ndo_nonempty) /// fluctuations over timestep iterations if DO_TITER is defined) to -1. void initialise_prev_titer_photoionestimators() { #ifdef DO_TITER - for (int nonemptymgi = 0; n < grid::get_npts_model(); n++) { - globals::ffheatingestimator_save[n] = -1.; - globals::colheatingestimator_save[n] = -1.; - if (grid::get_numassociatedcells(modelgridindex) > 0) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - J_reduced_save[nonemptymgi] = -1.; - nuJ_reduced_save[nonemptymgi] = -1.; - for (int element = 0; element < get_nelements(); element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions - 1; ion++) { - if constexpr (USE_LUT_PHOTOION) { - globals::gammaestimator_save[get_ionestimindex(n, element, ion)] = -1.; - } - if constexpr (USE_LUT_BFHEATING) { - globals::bfheatingestimator_save[get_ionestimindex(n, element, ion)] = -1.; - } + for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + globals::ffheatingestimator_save[nonemptymgi] = -1.; + globals::colheatingestimator_save[nonemptymgi] = -1.; + J_reduced_save[nonemptymgi] = -1.; + nuJ_reduced_save[nonemptymgi] = -1.; + for (int element = 0; element < get_nelements(); element++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions - 1; ion++) { + if constexpr (USE_LUT_PHOTOION) { + globals::gammaestimator_save[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)] = -1.; + } + if constexpr (USE_LUT_BFHEATING) { + globals::bfheatingestimator_save[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)] = -1.; } } } @@ -424,13 +450,6 @@ void initialise_prev_titer_photoionestimators() { auto get_Jblueindex(const int lineindex) -> int // returns -1 if the line does not have a Jblue estimator { - // slow linear search - // for (int i = 0; i < detailed_linecount; i++) - // { - // if (detailed_lineindicies[i] == lineindex) - // return i; - // } - if constexpr (!DETAILED_LINE_ESTIMATORS_ON) { return -1; } @@ -472,72 +491,6 @@ auto get_Jb_lu_contribcount(const int modelgridindex, const int jblueindex) -> i return prev_Jb_lu_normed[modelgridindex][jblueindex].contribcount; } -static auto get_bin_J(int modelgridindex, int binindex) -> double -// get the normalised J_nu -{ - const ptrdiff_t 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); - return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].J_raw * J_normfactor[nonemptymgi]; -} - -static auto get_bin_nuJ(int modelgridindex, int binindex) -> double { - const ptrdiff_t 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); - return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].nuJ_raw * J_normfactor[nonemptymgi]; -} - -static inline auto get_bin_nu_bar(int modelgridindex, int binindex) -> double -// importantly, this is average beween the current and previous timestep -{ - const double nuJ_sum = get_bin_nuJ(modelgridindex, binindex); - const double J_sum = get_bin_J(modelgridindex, binindex); - return nuJ_sum / J_sum; -} - -static inline auto get_bin_nu_lower(int binindex) -> double { - if (binindex > 0) { - return radfieldbin_nu_upper[binindex - 1]; - } - return nu_lower_first_initial; -} - -static inline auto get_bin_contribcount(int modelgridindex, int binindex) -> int { - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - return radfieldbins[nonemptymgi * RADFIELDBINCOUNT + binindex].contribcount; -} - -static inline auto get_bin_W(int modelgridindex, int binindex) -> float { - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - return radfieldbin_solutions[nonemptymgi * RADFIELDBINCOUNT + binindex].W; -} - -static inline auto get_bin_T_R(int modelgridindex, int binindex) -> float { - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - return radfieldbin_solutions[nonemptymgi * RADFIELDBINCOUNT + binindex].T_R; -} - -static inline auto select_bin(double nu) -> int { - if (nu < get_bin_nu_lower(0)) { - return -2; // out of range, nu lower than lowest bin's lower boundary - } - - // find the lowest frequency bin with radfieldbin_nu_upper > nu - auto *bin = std::upper_bound(&radfieldbin_nu_upper[0], &radfieldbin_nu_upper[RADFIELDBINCOUNT], nu); - const int binindex = std::distance(&radfieldbin_nu_upper[0], bin); - if (binindex >= RADFIELDBINCOUNT) { - // out of range, nu higher than highest bin's upper boundary - return -1; - } - - return binindex; -} - void write_to_file(int modelgridindex, int timestep) { assert_always(MULTIBIN_RADFIELD_MODEL_ON); const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); @@ -552,13 +505,13 @@ void write_to_file(int modelgridindex, int timestep) { } for (int binindex = -1 - detailed_linecount; binindex < RADFIELDBINCOUNT; binindex++) { - double nu_lower = 0.0; - double nu_upper = 0.0; - double nuJ_out = 0.0; - double J_out = 0.0; - float T_R = 0.0; - float W = 0.0; - double J_nu_bar = 0.0; + double nu_lower = 0.; + double nu_upper = 0.; + double nuJ_out = 0.; + double J_out = 0.; + float T_R = 0.; + float W = 0.; + double J_nu_bar = 0.; int contribcount = 0; const bool skipoutput = false; @@ -591,16 +544,11 @@ void write_to_file(int modelgridindex, int timestep) { W = -1.; J_nu_bar = prev_Jb_lu_normed[modelgridindex][jblueindex].value, contribcount = prev_Jb_lu_normed[modelgridindex][jblueindex].contribcount; - - // if (J_nu_bar <= 0.) - // { - // skipoutput = true; - // } } if (!skipoutput) { - fprintf(radfieldfile, "%d %d %d %11.5e %11.5e %9.3e %9.3e %9.3e %d %9.1f %12.5e\n", timestep, modelgridindex, - binindex, nu_lower, nu_upper, nuJ_out, J_out, J_nu_bar, contribcount, T_R, W); + fprintf(radfieldfile, "%d %d %d %.5e %.5e %.3e %.3e %.3e %d %.1f %.5e\n", timestep, modelgridindex, binindex, + nu_lower, nu_upper, nuJ_out, J_out, J_nu_bar, contribcount, T_R, W); } } fflush(radfieldfile); @@ -629,7 +577,6 @@ void close_file() { } if constexpr (DETAILED_BF_ESTIMATORS_ON) { - free(bfrate_raw); #ifdef MPI_ON if (win_radfieldbin_solutions != MPI_WIN_NULL) { MPI_Win_free(&win_prev_bfrate_normed); @@ -642,94 +589,85 @@ void close_file() { } } -void zero_estimators(int modelgridindex) +void zero_estimators() // set up the new bins and clear the estimators in preparation // for a timestep { - if (grid::get_numassociatedcells(modelgridindex) == 0) { - return; - } - - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + std::ranges::fill(J_normfactor, -1.0); + std::ranges::fill(J, 0.0); + std::ranges::fill(nuJ, 0.0); + std::ranges::fill(bfrate_raw, 0.0); - if constexpr (DETAILED_BF_ESTIMATORS_ON) { - assert_always(bfrate_raw != nullptr); - if (grid::get_numassociatedcells(modelgridindex) > 0) { - for (int i = 0; i < globals::bfestimcount; i++) { - bfrate_raw[nonemptymgi * globals::bfestimcount + i] = 0.; + if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { + assert_always(radfieldbins != nullptr); + for (ptrdiff_t nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { + for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { + const auto mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + radfieldbins[mgibinindex].J_raw = 0.; + radfieldbins[mgibinindex].nuJ_raw = 0.; + radfieldbins[mgibinindex].contribcount = 0; } } } if constexpr (DETAILED_LINE_ESTIMATORS_ON) { - assert_always(Jb_lu_raw != nullptr); - assert_always(Jb_lu_raw[modelgridindex] != nullptr); - for (int i = 0; i < detailed_linecount; i++) { - Jb_lu_raw[modelgridindex][i].value = 0.; - Jb_lu_raw[modelgridindex][i].contribcount = 0.; - } - } - - J[nonemptymgi] = 0.; // this is required even if FORCE_LTE is on - nuJ[nonemptymgi] = 0.; - - if (MULTIBIN_RADFIELD_MODEL_ON) { - // printout("radfield: zeroing estimators in %d bins in cell %d\n",RADFIELDBINCOUNT,modelgridindex); - - assert_always(radfieldbins != nullptr); - for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; - radfieldbins[mgibinindex].J_raw = 0.0; - radfieldbins[mgibinindex].nuJ_raw = 0.0; - radfieldbins[mgibinindex].contribcount = 0; + for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { + const auto modelgridindex = grid::get_mgi_of_nonemptymgi(nonemptymgi); + assert_always(Jb_lu_raw != nullptr); + assert_always(Jb_lu_raw[modelgridindex] != nullptr); + for (int i = 0; i < detailed_linecount; i++) { + Jb_lu_raw[modelgridindex][i].value = 0.; + Jb_lu_raw[modelgridindex][i].contribcount = 0.; + } } } - set_J_normfactor(modelgridindex, -1.0); } static void update_bfestimators(const int nonemptymgi, const double distance_e_cmf, const double nu_cmf, - const struct packet *const pkt_ptr) { + const double doppler_nucmf_on_nurf, const Phixslist &phixslist) { assert_testmodeonly(DETAILED_BF_ESTIMATORS_ON); - assert_always(bfrate_raw != nullptr); - if (distance_e_cmf == 0) { - return; - } + const double distance_e_cmf_over_nu = + distance_e_cmf / nu_cmf * doppler_nucmf_on_nurf; // TODO: Luke: why did I put a doppler factor here? - const int nbfcontinua = globals::nbfcontinua; - const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt_ptr->pos, pkt_ptr->dir, pkt_ptr->prop_time); - // const double dopplerfactor = 1.; + // I think the nu_cmf slightly differs from when the phixslist was calculated + // so the nu condition on this nu_cmf can truncate the list further compared to what was used in the calculation + // of phixslist.gamma_contr - const int tid = get_thread_num(); - const double distance_e_cmf_over_nu = - distance_e_cmf / nu_cmf * dopplerfactor; // TODO: Luke: why did I put a doppler factor here? - for (int allcontindex = 0; allcontindex < nbfcontinua; allcontindex++) { - const double nu_edge = globals::allcont_nu_edge[allcontindex]; - const double nu_max_phixs = nu_edge * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table - - if (nu_cmf >= nu_edge && nu_cmf <= nu_max_phixs) { - int bf_estimator = globals::allcont[allcontindex].bfestimindex; - if (bf_estimator >= 0) { - assert_testmodeonly(bf_estimator < globals::bfestimcount); - - safeadd(bfrate_raw[nonemptymgi * globals::bfestimcount + bf_estimator], - globals::phixslist[tid].gamma_contr[allcontindex] * distance_e_cmf_over_nu); - } + const int bfestimend = + std::distance(globals::bfestim_nu_edge.data(), + std::upper_bound(globals::bfestim_nu_edge.data(), + globals::bfestim_nu_edge.data() + phixslist.bfestimend, nu_cmf)); - } else if (nu_cmf < nu_edge) { - // list is sorted by nu_edge, so all remaining will have nu_cmf < nu_edge - break; - } + const int bfestimbegin = std::distance(globals::bfestim_nu_edge.data(), + std::lower_bound(globals::bfestim_nu_edge.data() + phixslist.bfestimbegin, + globals::bfestim_nu_edge.data() + bfestimend, nu_cmf, + [](const double nu_edge, const double nu_cmf) { + return nu_edge * last_phixs_nuovernuedge < nu_cmf; + })); + + const auto bfestimcount = globals::bfestimcount; + for (int bfestimindex = bfestimbegin; bfestimindex < bfestimend; bfestimindex++) { + atomicadd(bfrate_raw[nonemptymgi * bfestimcount + bfestimindex], + phixslist.gamma_contr[bfestimindex] * distance_e_cmf_over_nu); } } void update_estimators(const int nonemptymgi, const double distance_e_cmf, const double nu_cmf, - const struct packet *const pkt_ptr) { - safeadd(J[nonemptymgi], distance_e_cmf); - safeadd(nuJ[nonemptymgi], distance_e_cmf * nu_cmf); + const double doppler_nucmf_on_nurf, const Phixslist &phixslist, const bool thickcell) { + if (distance_e_cmf == 0) { + return; + } + + atomicadd(J[nonemptymgi], distance_e_cmf); + atomicadd(nuJ[nonemptymgi], distance_e_cmf * nu_cmf); + + if (thickcell) { + return; + } if constexpr (DETAILED_BF_ESTIMATORS_ON) { - update_bfestimators(nonemptymgi, distance_e_cmf, nu_cmf, pkt_ptr); + update_bfestimators(nonemptymgi, distance_e_cmf, nu_cmf, doppler_nucmf_on_nurf, phixslist); } if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { @@ -737,17 +675,10 @@ void update_estimators(const int nonemptymgi, const double distance_e_cmf, const if (binindex >= 0) { const ptrdiff_t 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); + atomicadd(radfieldbins[mgibinindex].J_raw, distance_e_cmf); + atomicadd(radfieldbins[mgibinindex].nuJ_raw, distance_e_cmf * nu_cmf); + atomicadd(radfieldbins[mgibinindex].contribcount, 1); } - // else - // { - // printout("WARNING: radfield::update_estimators dropping packet contribution for nu_cmf %g\n", - // nu_cmf); - // printout(" modelgridindex %d binindex %d nu_lower_first %g nu_upper_last %g \n", - // modelgridindex, binindex, nu_lower_first, get_bin_nu_upper(modelgridindex,RADFIELDBINCOUNT - 1)); - // } } } @@ -760,10 +691,6 @@ void update_lineestimator(const int modelgridindex, const int lineindex, const d if (jblueindex >= 0) { Jb_lu_raw[modelgridindex][jblueindex].value += increment; Jb_lu_raw[modelgridindex][jblueindex].contribcount += 1; - // const int lineindex = detailed_lineindicies[jblueindex]; - // printout(" increment cell %d lineindex %d Jb_lu_raw %g prev_Jb_lu_normed %g radfield(nu_trans) %g\n", - // modelgridindex, lineindex, Jb_lu_raw[modelgridindex][jblueindex], - // prev_Jb_lu_normed[modelgridindex][jblueindex].value, radfield(linelist[lineindex].nu, modelgridindex)); } } @@ -774,23 +701,12 @@ auto radfield(double nu, int modelgridindex) -> double if (globals::timestep >= FIRST_NLTE_RADFIELD_TIMESTEP) { const int binindex = select_bin(nu); if (binindex >= 0) { - const ptrdiff_t mgibinindex = grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex; - const struct radfieldbin_solution *const bin = &radfieldbin_solutions[mgibinindex]; - if (bin->W >= 0.) { - const double J_nu = dbb(nu, bin->T_R, bin->W); + const auto &bin = + radfieldbin_solutions[grid::get_modelcell_nonemptymgi(modelgridindex) * RADFIELDBINCOUNT + binindex]; + if (bin.W >= 0.) { + 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)) - // { - // // undiluted LTE blueward of the bins - // const double J_nu_LTE = dbb(nu, grid::get_Te(modelgridindex), 1.0); - // return J_nu_LTE; - // } - // else - // return 0; // no radfield redwards of the bins - // printout("WARNING: Radfield modelgridindex %d binindex %d nu %g nu_lower_first %g nu_upper_last %g \n", - // modelgridindex, binindex, nu, nu_lower_first, nu_upper_last); } return 0.; } @@ -802,33 +718,32 @@ auto radfield(double nu, int modelgridindex) -> double return J_nu_fullspec; } -constexpr auto gsl_integrand_planck(const double nu, void *paras) -> double { - const double T_R = (static_cast(paras))->T_R; - const enum_prefactor prefactor = (static_cast(paras))->prefactor; +constexpr auto gsl_integrand_planck(const double nu, void *voidparas) -> double { + const auto *paras = static_cast(voidparas); + const auto T_R = paras->T_R; double integrand = TWOHOVERCLIGHTSQUARED * std::pow(nu, 3) / (std::expm1(HOVERKB * nu / T_R)); - if (prefactor == TIMES_NU) { + if (paras->times_nu) { integrand *= nu; } return integrand; } -static auto planck_integral(double T_R, double nu_lower, double nu_upper, enum_prefactor prefactor) -> double { +static auto planck_integral(double T_R, double nu_lower, double nu_upper, const bool times_nu) -> double { double integral = 0.; double error = 0.; const double epsrel = 1e-10; const double epsabs = 0.; - gsl_planck_integral_paras intparas = {.T_R = T_R, .prefactor = prefactor}; - - const gsl_function F_planck = {.function = &gsl_integrand_planck, .params = &intparas}; + const gsl_planck_integral_paras intparas = {.T_R = T_R, .times_nu = times_nu}; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); - const int status = gsl_integration_qag(&F_planck, nu_lower, nu_upper, epsabs, epsrel, GSLWSIZE, GSL_INTEG_GAUSS61, - gslworkspace, &integral, &error); + + const int status = integrator(intparas, nu_lower, nu_upper, epsabs, epsrel, GSL_INTEG_GAUSS61, + &integral, &error); if (status != 0) { printout("planck_integral integrator status %d, GSL_FAILURE= %d. Integral value %g, setting to zero.\n", status, GSL_FAILURE, integral); @@ -839,10 +754,13 @@ static auto planck_integral(double T_R, double nu_lower, double nu_upper, enum_p return integral; } -static auto planck_integral_analytic(double T_R, double nu_lower, double nu_upper, enum_prefactor prefactor) -> double { +auto planck_integral_analytic(const double T_R, const double nu_lower, const double nu_upper, + const bool times_nu) -> double { + // return the integral of nu^3 / (exp(h nu / k T) - 1) from nu_lower to nu_upper + // or if times_nu is true, the integral of nu^4 / (exp(h nu / k T) - 1) from nu_lower to nu_upper double integral = 0.; - if (prefactor == TIMES_NU) { + if (times_nu) { 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.; @@ -878,33 +796,34 @@ static auto delta_nu_bar(double T_R, void *paras) -> double // 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; - const int binindex = (static_cast(paras))->binindex; + const auto *params = static_cast(paras); + const int modelgridindex = params->modelgridindex; + const int binindex = params->binindex; const double nu_lower = get_bin_nu_lower(binindex); const double nu_upper = get_bin_nu_upper(binindex); const double nu_bar_estimator = get_bin_nu_bar(modelgridindex, binindex); - const double nu_times_planck_numerical = planck_integral(T_R, nu_lower, nu_upper, TIMES_NU); - const double planck_integral_numerical = planck_integral(T_R, nu_lower, nu_upper, ONE); + const double nu_times_planck_numerical = planck_integral(T_R, nu_lower, nu_upper, true); + const double planck_integral_numerical = planck_integral(T_R, nu_lower, nu_upper, false); const double nu_bar_planck_T_R = nu_times_planck_numerical / planck_integral_numerical; - /*double nu_times_planck_integral = planck_integral_analytic(T_R, nu_lower, nu_upper, TIMES_NU); - double planck_integral_result = planck_integral_analytic(T_R, nu_lower, nu_upper, ONE); - double nu_bar_planck = nu_times_planck_integral / planck_integral_result; + // double nu_times_planck_integral = planck_integral_analytic(T_R, nu_lower, nu_upper, true); + // double planck_integral_result = planck_integral_analytic(T_R, nu_lower, nu_upper, false); + // double nu_bar_planck = nu_times_planck_integral / planck_integral_result; - //printout("nu_bar %g nu_bar_planck(T=%g) %g\n",nu_bar,T_R,nu_bar_planck); + // // printout("nu_bar %g nu_bar_planck(T=%g) %g\n",nu_bar,T_R,nu_bar_planck); - if (!std::isfinite(nu_bar_planck)) - { - double nu_times_planck_numerical = planck_integral(T_R, nu_lower, nu_upper, TIMES_NU); - double planck_integral_numerical = planck_integral(T_R, nu_lower, nu_upper, ONE); - double nu_bar_planck_numerical = nu_times_planck_numerical / planck_integral_numerical; + // if (!std::isfinite(nu_bar_planck)) { + // double nu_times_planck_numerical = planck_integral(T_R, nu_lower, nu_upper, true); + // double planck_integral_numerical = planck_integral(T_R, nu_lower, nu_upper, false); + // double nu_bar_planck_numerical = nu_times_planck_numerical / planck_integral_numerical; - printout("planck_integral_analytic is %g. Replacing with numerical result of - %g.\n",nu_bar_planck,nu_bar_planck_numerical); nu_bar_planck = nu_bar_planck_numerical; - }*/ + // printout("planck_integral_analytic is %g. Replacing with numerical result of %g.\n", nu_bar_planck, + // nu_bar_planck_numerical); + // nu_bar_planck = nu_bar_planck_numerical; + // } const double delta_nu_bar = nu_bar_planck_T_R - nu_bar_estimator; @@ -919,9 +838,9 @@ static auto delta_nu_bar(double T_R, void *paras) -> double } static auto find_T_R(int modelgridindex, int binindex) -> float { - double T_R = 0.0; + double T_R = 0.; - gsl_T_R_solver_paras paras; + gsl_T_R_solver_paras paras{}; paras.modelgridindex = modelgridindex; paras.binindex = binindex; @@ -943,9 +862,7 @@ static auto find_T_R(int modelgridindex, int binindex) -> float { const double epsabs = 0.; const int maxit = 100; - gsl_function find_T_R_f; - find_T_R_f.function = &delta_nu_bar; - find_T_R_f.params = ¶s; + gsl_function find_T_R_f = {.function = &delta_nu_bar, .params = ¶s}; /// one dimensional gsl root solver, bracketing type gsl_root_fsolver *T_R_solver = gsl_root_fsolver_alloc(gsl_root_fsolver_brent); @@ -985,7 +902,7 @@ static auto find_T_R(int modelgridindex, int binindex) -> float { } return T_R; -} +} // namespace radfield static void set_params_fullspec(const int modelgridindex, const int timestep) { const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); @@ -1033,13 +950,13 @@ void fit_parameters(int modelgridindex, int timestep) { set_params_fullspec(modelgridindex, timestep); - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { if (J_normfactor[nonemptymgi] <= 0) { printout("radfield: FATAL J_normfactor = %g in cell %d at call to fit_parameters", J_normfactor[nonemptymgi], modelgridindex); - abort(); + std::abort(); } double J_bin_sum = 0.; @@ -1060,13 +977,11 @@ void fit_parameters(int modelgridindex, int timestep) const double nu_lower = get_bin_nu_lower(binindex); const double nu_upper = get_bin_nu_upper(binindex); const double J_bin = get_bin_J(modelgridindex, binindex); - float T_R_bin = -1.0; - double W_bin = -1.0; + float T_R_bin = -1.; + double W_bin = -1.; const int contribcount = get_bin_contribcount(modelgridindex, binindex); if (contribcount > 0) { - // // enum_bin_fit_type bin_fit_type = radfieldbin_solutions[modelgridindex][binindex].fit_type; - // if (bin_fit_type == FIT_DILUTE_BLACKBODY) { T_R_bin = find_T_R(modelgridindex, binindex); @@ -1077,7 +992,7 @@ void fit_parameters(int modelgridindex, int timestep) T_R_bin = T_e; } - double planck_integral_result = planck_integral(T_R_bin, nu_lower, nu_upper, ONE); + double planck_integral_result = planck_integral(T_R_bin, nu_lower, nu_upper, false); // printout("planck_integral(T_R=%g, nu_lower=%g, nu_upper=%g) = %g\n", T_R_bin, nu_lower, // nu_upper, planck_integral_result); @@ -1087,11 +1002,11 @@ void fit_parameters(int modelgridindex, int timestep) // printout("T_R_bin %g, nu_lower %g, nu_upper %g\n", T_R_bin, nu_lower, nu_upper); printout("W %g too high, trying setting T_R of bin %d to %g. J_bin %g planck_integral %g\n", W_bin, binindex, T_R_max, J_bin, planck_integral_result); - planck_integral_result = planck_integral(T_R_max, nu_lower, nu_upper, ONE); + planck_integral_result = planck_integral(T_R_max, nu_lower, nu_upper, false); W_bin = J_bin / planck_integral_result; if (W_bin > 1e4) { printout("W still very high, W=%g. Zeroing bin...\n", W_bin); - T_R_bin = -99.0; + T_R_bin = -99.; W_bin = 0.; } else { printout("new W is %g. Continuing with this value\n", W_bin); @@ -1099,57 +1014,21 @@ void fit_parameters(int modelgridindex, int timestep) } } } - // else if (bin_fit_type == FIT_CONSTANT) - // { - // T_R_bin = -1.; - // W_bin = J_bin / (nu_upper - nu_lower); - // } - // else - // { - // printout("fit_parameters: unknown fit type %d for bin %d\n", bin_fit_type, binindex); - // T_R_bin = -1.; - // W_bin = -1.; - // } } else { T_R_bin = 0.; W_bin = 0.; } - // else - // { - // T_R_bin = -1; - // W_bin = -1; - // } - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + + const auto mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; radfieldbin_solutions[mgibinindex].T_R = T_R_bin; radfieldbin_solutions[mgibinindex].W = W_bin; } - // double prev_nu_upper = nu_lower_first_initial; - // for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) - // { - // const double J_bin = get_bin_J(modelgridindex,binindex); - // const double T_R_bin = get_bin_T_R(modelgridindex,binindex); - // const double W_bin = get_bin_W(modelgridindex,binindex); - // const int contribcount = get_bin_contribcount(modelgridindex, binindex); - // const double bin_nu_upper = get_bin_nu_upper(binindex); - // const double nubar = get_bin_nu_bar(modelgridindex, binindex); - // - // printout("bin %4d (lambda %7.1f Ã… to %7.1f Ã…): contribcount %5d J %7.1e T_R %8.1f W %12.5e lambdabar %7.1f - // Ã…\n", - // binindex, 1e8 * CLIGHT / prev_nu_upper, 1e8 * CLIGHT / bin_nu_upper, contribcount, J_bin, T_R_bin, - // W_bin, 1e8 * CLIGHT / nubar); - // - // prev_nu_upper = get_bin_nu_upper(binindex); - // } - write_to_file(modelgridindex, timestep); } } -void set_J_normfactor(int modelgridindex, double normfactor) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - J_normfactor[nonemptymgi] = normfactor; -} +void set_J_normfactor(const int nonemptymgi, const double normfactor) { J_normfactor[nonemptymgi] = normfactor; } void normalise_J(const int modelgridindex, const double estimator_normfactor_over4pi) { const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); @@ -1161,14 +1040,14 @@ void normalise_J(const int modelgridindex, const double estimator_normfactor_ove } } -void normalise_bf_estimators(const int modelgridindex, const double estimator_normfactor_over_H) { +void normalise_bf_estimators(const int modelgridindex, const ptrdiff_t nonemptymgi, + const double estimator_normfactor_over_H) { if constexpr (DETAILED_BF_ESTIMATORS_ON) { printout("normalise_bf_estimators for cell %d with factor %g\n", modelgridindex, estimator_normfactor_over_H); - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); assert_always(nonemptymgi >= 0); for (int i = 0; i < globals::bfestimcount; i++) { - const auto detailed_mgibfindex = nonemptymgi * globals::bfestimcount + i; - prev_bfrate_normed[detailed_mgibfindex] = bfrate_raw[detailed_mgibfindex] * estimator_normfactor_over_H; + const auto mgibfindex = nonemptymgi * globals::bfestimcount + i; + prev_bfrate_normed[mgibfindex] = bfrate_raw[mgibfindex] * estimator_normfactor_over_H; } } } @@ -1176,12 +1055,11 @@ 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? - 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); + const auto bfcontindex = std::distance( + globals::allcont, 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); + })); if (bfcontindex < globals::nbfcontinua) { return bfcontindex; @@ -1192,20 +1070,18 @@ static auto get_bfcontindex(const int element, const int lowerion, const int low auto get_bfrate_estimator(const int element, const int lowerion, const int lower, const int phixstargetindex, const int modelgridindex) -> double { - if constexpr (!DETAILED_BF_ESTIMATORS_ON) { - return -1; - } else { + if constexpr (DETAILED_BF_ESTIMATORS_ON) { const int allcontindex = get_bfcontindex(element, lowerion, lower, phixstargetindex); if (allcontindex >= 0) { - const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - const int bfestimindex = globals::allcont[allcontindex].bfestimindex; - return (bfestimindex >= 0) ? prev_bfrate_normed[nonemptymgi * globals::bfestimcount + bfestimindex] : 0.; + const auto bfestimindex = globals::allcont[allcontindex].bfestimindex; + if (bfestimindex >= 0) { + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + return prev_bfrate_normed[nonemptymgi * globals::bfestimcount + bfestimindex]; + } } - - printout("no bf rate for element Z=%d ion_stage %d lower %d phixstargetindex %d\n", get_atomicnumber(element), - get_ionstage(element, lowerion), lower, phixstargetindex); - return -1.; } + + return -1.; } void normalise_nuJ(const int modelgridindex, const double estimator_normfactor_over4pi) { @@ -1257,35 +1133,37 @@ void titer_nuJ(const int modelgridindex) { void reduce_estimators() // reduce and broadcast (allreduce) the estimators for J and nuJ in all bins { - const int nonempty_npts_model = grid::get_nonempty_npts_model(); + const ptrdiff_t 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::bfestimcount, MPI_DOUBLE, - MPI_SUM, MPI_COMM_WORLD); + for (ptrdiff_t nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { + MPI_Allreduce(MPI_IN_PLACE, &bfrate_raw[nonemptymgi * globals::bfestimcount], globals::bfestimcount, MPI_DOUBLE, + MPI_SUM, MPI_COMM_WORLD); + } } if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - const time_t sys_time_start_reduction = time(nullptr); + const auto sys_time_start_reduction = std::time(nullptr); printout("Reducing binned radiation field estimators"); assert_always(radfieldbins != nullptr); - for (int nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { + for (ptrdiff_t nonemptymgi = 0; nonemptymgi < nonempty_npts_model; nonemptymgi++) { for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + const auto 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; + const int duration_reduction = std::time(nullptr) - sys_time_start_reduction; printout(" (took %d s)\n", duration_reduction); } if constexpr (DETAILED_LINE_ESTIMATORS_ON) { - const time_t sys_time_start_reduction = time(nullptr); + const auto sys_time_start_reduction = std::time(nullptr); printout("Reducing detailed line estimators"); for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { @@ -1298,7 +1176,7 @@ void reduce_estimators() } } } - const int duration_reduction = time(nullptr) - sys_time_start_reduction; + const int duration_reduction = std::time(nullptr) - sys_time_start_reduction; printout(" (took %d s)\n", duration_reduction); } MPI_Barrier(MPI_COMM_WORLD); @@ -1317,14 +1195,11 @@ void do_MPI_Bcast(const int modelgridindex, const int root, int root_node_id) if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + const auto 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); } } @@ -1356,7 +1231,7 @@ void write_restart_data(FILE *gridsave_file) { T_R_min, T_R_max); for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - fprintf(gridsave_file, "%d %la\n", binindex, radfieldbin_nu_upper[binindex]); + fprintf(gridsave_file, "%d %la\n", binindex, get_bin_nu_upper(binindex)); } } @@ -1394,11 +1269,10 @@ void write_restart_data(FILE *gridsave_file) { if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + const auto mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; fprintf(gridsave_file, "%la %la %a %a %d\n", radfieldbins[mgibinindex].J_raw, radfieldbins[mgibinindex].nuJ_raw, radfieldbin_solutions[mgibinindex].W, radfieldbin_solutions[mgibinindex].T_R, radfieldbins[mgibinindex].contribcount); - // radfieldbins[mgibinindex].fit_type } } @@ -1420,14 +1294,14 @@ void read_restart_data(FILE *gridsave_file) { assert_always(fscanf(gridsave_file, "%d\n", &code_check) == 1); if (code_check != 30490824) { printout("ERROR: Beginning of radfield restart data not found! Found %d instead of 30490824\n", code_check); - abort(); + std::abort(); } if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { - double T_R_min_in = NAN; - double T_R_max_in = NAN; - double nu_lower_first_initial_in = NAN; - double nu_upper_last_initial_in = NAN; + double T_R_min_in{NAN}; + double T_R_max_in{NAN}; + double nu_lower_first_initial_in{NAN}; + double nu_upper_last_initial_in{NAN}; int bincount_in = 0; assert_always(fscanf(gridsave_file, "%d %la %la %la %la\n", &bincount_in, &nu_lower_first_initial_in, &nu_upper_last_initial_in, &T_R_min_in, &T_R_max_in) == 5); @@ -1450,13 +1324,15 @@ void read_restart_data(FILE *gridsave_file) { bincount_in, nu_lower_first_initial_in, nu_upper_last_initial_in, T_R_min_in, T_R_max_in); printout("require %d bins, nu_lower_first_initial %lg nu_upper_last_initial %lg T_R_min %lg T_R_max %lg\n", RADFIELDBINCOUNT, nu_lower_first_initial, nu_upper_last_initial, T_R_min, T_R_max); - abort(); + std::abort(); } for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { int binindex_in = 0; - assert_always(fscanf(gridsave_file, "%d %la\n", &binindex_in, &radfieldbin_nu_upper[binindex]) == 2); + double nu_upper_in = NAN; + assert_always(fscanf(gridsave_file, "%d %la\n", &binindex_in, &nu_upper_in) == 2); assert_always(binindex_in == binindex); + assert_always(nu_upper_in == get_bin_nu_upper(binindex)); } } @@ -1479,12 +1355,8 @@ void read_restart_data(FILE *gridsave_file) { float bfrate_normed = 0; assert_always(fscanf(gridsave_file, "%a ", &bfrate_normed) == 1); - const auto mgibfindex = nonemptymgi * globals::bfestimcount + i; -#ifdef MPI_ON - if (globals::rank_in_node == 0) -#endif - { - prev_bfrate_normed[mgibfindex] = bfrate_normed; + if (globals::rank_in_node == 0) { + prev_bfrate_normed[nonemptymgi * globals::bfestimcount + i] = bfrate_normed; } } } @@ -1498,7 +1370,7 @@ void read_restart_data(FILE *gridsave_file) { if (detailed_linecount_in != detailed_linecount) { printout("ERROR: gridsave file specifies %d detailed lines but this simulation has %d.\n", detailed_linecount_in, detailed_linecount); - abort(); + std::abort(); } for (int jblueindex = 0; jblueindex < detailed_linecount; jblueindex++) { @@ -1508,17 +1380,17 @@ void read_restart_data(FILE *gridsave_file) { for (int modelgridindex = 0; modelgridindex < grid::get_npts_model(); modelgridindex++) { if (grid::get_numassociatedcells(modelgridindex) > 0) { - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const ptrdiff_t nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); int mgi_in = 0; 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(); + std::abort(); } if constexpr (MULTIBIN_RADFIELD_MODEL_ON) { for (int binindex = 0; binindex < RADFIELDBINCOUNT; binindex++) { - const ptrdiff_t mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; + const auto mgibinindex = nonemptymgi * RADFIELDBINCOUNT + binindex; float W = 0; float T_R = 0; assert_always(fscanf(gridsave_file, "%la %la %a %a %d\n", &radfieldbins[mgibinindex].J_raw, @@ -1545,49 +1417,8 @@ void read_restart_data(FILE *gridsave_file) { assert_always(fscanf(gridsave_file, "%d\n", &code_check) == 1); if (code_check != 42809403) { printout("ERROR: End of radfield restart data not found! Found %d instead of 42809403\n", code_check); - abort(); + std::abort(); } } -// not in use, but could potential improve speed and accuracy of integrating -// 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::timestep >= FIRST_NLTE_RADFIELD_TIMESTEP)) { - auto *pts = static_cast(malloc((RADFIELDBINCOUNT + 3) * sizeof(double))); - int binindex_a = select_bin(nu_a); - 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 - { - // region doesn't contain any bins - pts[npts++] = nu_b; - } else { - if (binindex_a < 0) // a is below the first bin - { - binindex_a = 0; - pts[npts++] = get_bin_nu_lower(0); - } - - const int maxbinplusone = (binindex_b < 0) ? RADFIELDBINCOUNT : binindex_b; - - for (int binindex = binindex_a; binindex < maxbinplusone; binindex++) { - pts[npts++] = get_bin_nu_upper(binindex); - } - - pts[npts++] = nu_b; - } - // for (int e = 0; e < npts; e++) - // { - // printout("radfield::integrate singular point number %d at nu %g, (nu_a %g, nu_b %g), low %g high %g\n", - // e, pts[e], nu_a, nu_b, radfield(pts[e] * 0.9999, 0), radfield(pts[e] * 1.0001, 0)); - // } - const int status = gsl_integration_qagp(f, pts, npts, epsabs, epsrel, limit, workspace, result, abserr); - free(pts); - return status; - } - return gsl_integration_qag(f, nu_a, nu_b, epsabs, epsrel, limit, key, workspace, result, abserr); -} - } // namespace radfield \ No newline at end of file diff --git a/radfield.h b/radfield.h index 052dae006..6c32c675a 100644 --- a/radfield.h +++ b/radfield.h @@ -1,3 +1,5 @@ +#pragma once +#include #ifndef RADFIELD_H #define RADFIELD_H @@ -5,20 +7,21 @@ #include -#include "sn3d.h" +#include "rpkt.h" namespace radfield { -void zero_estimators(int modelgridindex); +void zero_estimators(); void init(int my_rank, int ndo_nonempty); void initialise_prev_titer_photoionestimators(); void write_to_file(int modelgridindex, int timestep); void close_file(); -void update_estimators(int nonemptymgi, double distance_e_cmf, double nu_cmf, const struct packet *pkt_ptr); +void update_estimators(int nonemptymgi, double distance_e_cmf, double nu_cmf, double doppler_nucmf_on_nurf, + const Phixslist &phixslist, bool thickcell); void update_lineestimator(int modelgridindex, int lineindex, double increment); [[nodiscard]] auto radfield(double nu, int modelgridindex) -> double; void fit_parameters(int modelgridindex, int timestep); -void set_J_normfactor(int modelgridindex, double normfactor); +void set_J_normfactor(int nonemptymgi, double normfactor); void normalise_J(int modelgridindex, double estimator_normfactor_over4pi); void normalise_nuJ(int modelgridindex, double estimator_normfactor_over4pi); [[nodiscard]] auto get_T_J_from_J(int modelgridindex) -> double; @@ -31,13 +34,16 @@ void reduce_estimators(); void do_MPI_Bcast(int modelgridindex, int root, int root_node_id); void write_restart_data(FILE *gridsave_file); void read_restart_data(FILE *gridsave_file); -void normalise_bf_estimators(int modelgridindex, double estimator_normfactor_over_H); -auto get_bfrate_estimator(int element, int lowerion, int lower, int phixstargetindex, int modelgridindex) -> double; +void normalise_bf_estimators(int modelgridindex, ptrdiff_t nonemptymgi, double estimator_normfactor_over_H); +[[nodiscard]] auto get_bfrate_estimator(int element, int lowerion, int lower, int phixstargetindex, + int modelgridindex) -> double; void print_bfrate_contributions(int element, int lowerion, int lower, int phixstargetindex, int modelgridindex, double nnlowerlevel, double nnlowerion); void reset_bfrate_contributions(int modelgridindex); -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; +[[nodiscard]] 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; +auto planck_integral_analytic(double T_R, double nu_lower, double nu_upper, bool times_nu) -> double; [[nodiscard]] constexpr auto dbb(double nu, auto T, auto W) -> double // returns J_nu [ergs/s/sr/cm2/Hz] for a dilute black body with temperature T and dilution factor W diff --git a/ratecoeff.cc b/ratecoeff.cc index c7420387c..1bb2e518e 100644 --- a/ratecoeff.cc +++ b/ratecoeff.cc @@ -1,16 +1,26 @@ #include "ratecoeff.h" +#ifdef MPI_ON +#include +#endif + #include +#include #include #include +#include // #define D_POSIX_SOURCE #include #include +#include +#include #include "artisoptions.h" #include "atomic.h" +#include "constants.h" +#include "globals.h" #include "grid.h" #include "ltepop.h" #include "macroatom.h" @@ -19,26 +29,14 @@ #include "rpkt.h" #include "sn3d.h" -// typedef struct gslintegration_ffheatingparas -// { -// double T_e; -// int cellnumber; -// } gslintegration_ffheatingparas; - -// typedef struct gslintegration_bfheatingparas -// { -// double nu_edge; -// int cellnumber; -// } gslintegration_bfheatingparas; +namespace { -double T_step_log; - -static double *spontrecombcoeffs = nullptr; +double *spontrecombcoeffs{}; // for USE_LUT_PHOTOION = true -static double *corrphotoioncoeffs = nullptr; +double *corrphotoioncoeffs{}; -static double *bfcooling_coeffs = nullptr; +double *bfcooling_coeffs{}; using gsl_integral_paras_gammacorr = struct { double nu_edge; @@ -48,17 +46,20 @@ using gsl_integral_paras_gammacorr = struct { int modelgridindex; }; -static char adatafile_hash[33]; -static char compositionfile_hash[33]; +char adatafile_hash[33]; +char compositionfile_hash[33]; std::array phixsfile_hash; +} // anonymous namespace + 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; + MPI_Aint size = + (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * static_cast(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); @@ -70,7 +71,8 @@ void setup_photoion_luts() { if constexpr (USE_LUT_PHOTOION) { #ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + size = + (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * static_cast(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); @@ -83,7 +85,8 @@ void setup_photoion_luts() { if constexpr (USE_LUT_BFHEATING) { #ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + size = + (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * static_cast(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); @@ -95,7 +98,7 @@ void setup_photoion_luts() { } #ifdef MPI_ON - size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * sizeof(double) : 0; + size = (globals::rank_in_node == 0) ? TABLESIZE * globals::nbfcontinua * static_cast(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); @@ -237,10 +240,10 @@ 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 in_alpha_sp = NAN; - double in_bfcooling_coeff = NAN; - double in_corrphotoioncoeff = NAN; - double in_bfheating_coeff = NAN; + 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); @@ -256,7 +259,7 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool } else { printout( "ERROR: USE_LUT_PHOTOION is on, but there are no corrphotoioncoeff values in ratecoeff file\n"); - abort(); + std::abort(); } } if constexpr (USE_LUT_BFHEATING) { @@ -267,7 +270,7 @@ static auto read_ratecoeff_dat(FILE *ratecoeff_file) -> bool printout( "ERROR: USE_LUT_BFHEATING is on, but there are no bfheating_coeff values in the ratecoeff " "file\n"); - abort(); + std::abort(); } } } @@ -304,7 +307,8 @@ static void write_ratecoeff_dat() { const int nlevels = get_ionisinglevels(element, ion); for (int level = 0; level < nlevels; level++) { /// Loop over the phixs targets - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { /// Loop over the temperature grid for (int iter = 0; iter < TABLESIZE; iter++) { const int bflutindex = get_bflutindex(iter, element, ion, level, phixstargetindex); @@ -324,7 +328,7 @@ static auto alpha_sp_integrand_gsl(const double nu, void *const voidparas) -> do /// Integrand to calculate the rate coefficient for spontaneous recombination /// using gsl integrators. { - const gslintegration_paras *const params = static_cast(voidparas); + const auto *const params = static_cast(voidparas); const float sigma_bf = photoionization_crosssection_fromtable(params->photoion_xs, params->nu_edge, nu); const double x = TWOOVERCLIGHTSQUARED * sigma_bf * pow(nu, 2) * exp(-HOVERKB * nu / params->T); @@ -339,7 +343,7 @@ static auto alpha_sp_E_integrand_gsl(const double nu, void *const voidparas) -> /// Integrand to calculate the rate coefficient for spontaneous recombination /// using gsl integrators. { - const gslintegration_paras *const params = static_cast(voidparas); + const auto *const params = static_cast(voidparas); const float T = params->T; const double nu_edge = params->nu_edge; @@ -357,7 +361,7 @@ static auto gammacorr_integrand_gsl(const double nu, void *const voidparas) -> d /// Integrand to calculate the rate coefficient for photoionization /// using gsl integrators. Corrected for stimulated recombination. { - const gslintegration_paras *const params = static_cast(voidparas); + const auto *const params = static_cast(voidparas); const float T = params->T; const double nu_edge = params->nu_edge; @@ -376,7 +380,7 @@ 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. { - const gslintegration_paras *const params = static_cast(voidparas); + const auto *const params = static_cast(voidparas); const float T = params->T; const double nu_edge = params->nu_edge; @@ -386,16 +390,6 @@ static auto approx_bfheating_integrand_gsl(const double nu, void *const voidpara /// Precalculation for T_e=T_R and W=1 const double x = sigma_bf * (1 - nu_edge / nu) * radfield::dbb(nu, T, 1) * (1 - exp(-HOVERKB * nu / T)); - /// Precalculation for a (T_R,T_e)-grid, but still W is assumed to be 1. - /// The radfield part can be corrected later because of its linear dependence. - /// But not the W in the stimulated correction term! - /*double T_e = ((gslintegration_paras *) paras)->T; - double T_R = ((gslintegration_paras *) paras)->T2; - double E_threshold = nu_edge*H; - double sf_Te = calculate_sahafact(element,ion,level,upperionlevel,T_e,E_threshold); - double sf_TR = calculate_sahafact(element,ion,level,upperionlevel,T_R,E_threshold); - x = sigma_bf*(1-nu_edge/nu)*radfield::dbb(nu,T_R,1) * (1 - sqrt(T_e/T_R) * sf_Te/sf_TR * exp(-H*nu/KB/T_e));*/ - return x; } @@ -405,7 +399,7 @@ static auto bfcooling_integrand_gsl(const double nu, void *const voidparas) -> d /// formula. The radiation fields dependence on W is taken into account by multiplying /// the resulting expression with the correct W later on. { - const gslintegration_paras *const params = static_cast(voidparas); + const auto *const params = static_cast(voidparas); const float T = params->T; const double nu_edge = params->nu_edge; @@ -416,60 +410,6 @@ static auto bfcooling_integrand_gsl(const double nu, void *const voidparas) -> d return sigma_bf * (nu - nu_edge) * TWOHOVERCLIGHTSQUARED * nu * nu * exp(-HOVERKB * nu / T); } -/*static double bfcooling_integrand_gsl_2(double nu, void *paras) -/// 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 -/// formula. The radiation fields dependence on W is taken into account by multiplying -/// the resulting expression with the correct W later on. -{ - 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); - - return sigma_bf*(1/nu_edge-1/nu) * TWOOVERCLIGHTSQUARED*pow(nu,3) * exp(-HOVERKB*nu/T); -}*/ - -/*static double stimulated_bfcooling_integrand_gsl(double nu, void *paras) -/// Integrand to precalculate the bound-free heating ratecoefficient in an approximate way -/// on a temperature grid using the assumption that T_e=T_R and W=1 in the ionisation -/// formula. The radiation fields dependence on W is taken into account by multiplying -/// the resulting expression with the correct W later on. -{ - 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); - - return sigma_bf * (1-nu_edge/nu) * radfield::dbb(nu, T, 1) * exp(-HOVERKB*nu/T); -}*/ - -/*static double stimulated_recomb_integrand_gsl(double nu, void *paras) -/// Integrand to calculate the rate coefficient for spontaneous recombination -/// using gsl integrators. -{ - double T = ((gslintegration_paras *) paras)->T; - double nu_edge = ((gslintegration_paras *) paras)->nu_edge; - - //double nu_edge = (epsilon(element,ion+1,0)-epsilon(element,ion,level))/H; - /// 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); - double x = sigma_bf / H / nu * radfield::dbb(nu,T,1) * exp(-HOVERKB*nu/T); - ///in formula this looks like - ///x = sigma_bf/H/nu * 2*H*pow(nu,3)/pow(CLIGHT,2) * exp(-H*nu/KB/T); - - ///set contributions from Lyman continuum artificially to zero to overcome it's large opacity - return x; -}*/ - static void precalculate_rate_coefficient_integrals() { // target fractional accuracy of the integrator //=1e-5 took 8 hours with Fe I to V! const double epsrelwarning = 1e-2; // fractional error to emit a warning @@ -481,13 +421,10 @@ static void precalculate_rate_coefficient_integrals() { #pragma omp parallel for #endif for (int ion = 0; ion < nions; ion++) { - // nlevels = get_nlevels(element,ion); const int atomic_number = get_atomicnumber(element); const int ionstage = get_ionstage(element, ion); const int nlevels = get_ionisinglevels(element, ion); - /// That's only an option for pure LTE - // if (TAKE_N_BFCONTINUA < nlevels) nlevels = TAKE_N_BFCONTINUA; - printout("Performing rate integrals for Z = %d, ion_stage %d...\n", atomic_number, ionstage); + printout("Performing rate integrals for Z = %d, ionstage %d...\n", atomic_number, ionstage); gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); @@ -506,9 +443,6 @@ static void precalculate_rate_coefficient_integrals() { const int upperlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); const double phixstargetprobability = get_phixsprobability(element, ion, level, phixstargetindex); - // printout("element %d, ion %d, level %d, upperlevel %d, epsilon %g, continuum %g, nlevels - // %d\n",element,ion,level,upperlevel,epsilon(element,ion,level),epsilon(element,ion+1,upperlevel),nlevels); - // const double E_threshold = epsilon(element,ion+1,upperlevel) - epsilon(element,ion,level); const double E_threshold = get_phixs_threshold(element, ion, level, phixstargetindex); const double nu_threshold = E_threshold / H; @@ -517,37 +451,25 @@ static void precalculate_rate_coefficient_integrals() { // Loop over the temperature grid for (int iter = 0; iter < TABLESIZE; iter++) { const int bflutindex = get_bflutindex(iter, element, ion, level, phixstargetindex); - double error = NAN; + double error{NAN}; int status = 0; const float T_e = MINTEMP * exp(iter * T_step_log); const double sfac = calculate_sahafact(element, ion, level, upperlevel, T_e, E_threshold); - // printout("%d %g\n",iter,T_e); assert_always(globals::elements[element].ions[ion].levels[level].photoion_xs != nullptr); // the threshold of the first target gives nu of the first phixstable point - gslintegration_paras intparas = { + const GSLIntegrationParas intparas = { .nu_edge = nu_threshold, .T = T_e, .photoion_xs = globals::elements[element].ions[ion].levels[level].photoion_xs}; - // gsl_function F_gamma; - // F_gamma.function = &gamma_integrand_gsl; - // F_gamma.params = &intparas; - // gsl_function F_alpha_sp_E; - // F_alpha_sp_E.function = &alpha_sp_E_integrand_gsl; - // F_alpha_sp_E.params = &intparas; - // F_stimulated_bfcooling.function = &stimulated_bfcooling_integrand_gsl; - // F_stimulated_bfcooling.params = &intparas; - // F_stimulated_recomb.function = &stimulated_recomb_integrand_gsl; - // F_stimulated_recomb.params = &intparas; - /// Spontaneous recombination and bf-cooling coefficient don't depend on the cutted radiation field - double alpha_sp = 0.0; - const gsl_function F_alpha_sp = {.function = &alpha_sp_integrand_gsl, .params = &intparas}; + double alpha_sp = 0.; - status = gsl_integration_qag(&F_alpha_sp, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &alpha_sp, &error); + status = + integrator(intparas, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, + GSL_INTEG_GAUSS61, &alpha_sp, &error); if (status != 0 && (status != 18 || (error / alpha_sp) > epsrelwarning)) { printout("alpha_sp integrator status %d. Integral value %9.3e +/- %9.3e\n", status, alpha_sp, error); } @@ -556,25 +478,18 @@ static void precalculate_rate_coefficient_integrals() { if (!std::isfinite(alpha_sp) || alpha_sp < 0) { printout( "WARNING: alpha_sp was negative or non-finite for level %d Te %g. alpha_sp %g sfac %g " - "phixstargetindex %d " - "phixstargetprobability %g\n", + "phixstargetindex %d phixstargetprobability %g\n", level, T_e, alpha_sp, sfac, phixstargetindex, phixstargetprobability); alpha_sp = 0; } - // assert_always(alpha_sp >= 0); 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 - // (integral %g, sahafac %g)\n", element, ion, level, upperlevel, T_e, alpha_sp, alpha_sp/(FOURPI * sfac * - // phixstargetprobability),sfac); - if constexpr (USE_LUT_PHOTOION) { - double gammacorr = 0.0; - const gsl_function F_gammacorr = {.function = &gammacorr_integrand_gsl, .params = &intparas}; + double gammacorr = 0.; - status = gsl_integration_qag(&F_gammacorr, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &gammacorr, &error); + status = integrator(intparas, nu_threshold, nu_max_phixs, 0, + RATECOEFF_INTEGRAL_ACCURACY, GSL_INTEG_GAUSS61, &gammacorr, + &error); if (status != 0 && (status != 18 || (error / gammacorr) > epsrelwarning)) { printout("gammacorr integrator status %d. Integral value %9.3e +/- %9.3e\n", status, gammacorr, error); } @@ -588,11 +503,11 @@ static void precalculate_rate_coefficient_integrals() { } if constexpr (USE_LUT_BFHEATING) { - double this_bfheating_coeff = 0.0; - const gsl_function F_bfheating = {.function = &approx_bfheating_integrand_gsl, .params = &intparas}; + double this_bfheating_coeff = 0.; - status = gsl_integration_qag(&F_bfheating, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &this_bfheating_coeff, &error); + status = integrator(intparas, nu_threshold, nu_max_phixs, 0, + RATECOEFF_INTEGRAL_ACCURACY, GSL_INTEG_GAUSS61, + &this_bfheating_coeff, &error); if (status != 0 && (status != 18 || (error / this_bfheating_coeff) > epsrelwarning)) { printout("bfheating_coeff integrator status %d. Integral value %9.3e +/- %9.3e\n", status, @@ -606,11 +521,11 @@ static void precalculate_rate_coefficient_integrals() { globals::bfheating_coeff[bflutindex] = this_bfheating_coeff; } - double this_bfcooling_coeff = 0.0; - const gsl_function F_bfcooling = {.function = &bfcooling_integrand_gsl, .params = &intparas}; + double this_bfcooling_coeff = 0.; - status = gsl_integration_qag(&F_bfcooling, nu_threshold, nu_max_phixs, 0, RATECOEFF_INTEGRAL_ACCURACY, - GSLWSIZE, GSL_INTEG_GAUSS61, gslworkspace, &this_bfcooling_coeff, &error); + status = integrator(intparas, nu_threshold, nu_max_phixs, 0, + RATECOEFF_INTEGRAL_ACCURACY, GSL_INTEG_GAUSS61, + &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, this_bfcooling_coeff, error); @@ -641,22 +556,21 @@ auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel const int npieces = globals::NPHIXSPOINTS; - gslintegration_paras intparas = {.nu_edge = nu_threshold, - .T = T_e, - .photoion_xs = globals::elements[element].ions[lowerion].levels[lower].photoion_xs}; - - const gsl_function F_alpha_sp = {.function = &alpha_sp_E_integrand_gsl, .params = &intparas}; + const GSLIntegrationParas intparas = { + .nu_edge = nu_threshold, + .T = T_e, + .photoion_xs = globals::elements[element].ions[lowerion].levels[lower].photoion_xs}; const double zrand = 1. - rng_uniform(); // Make sure that 0 < zrand <= 1 const double deltanu = (nu_max_phixs - nu_threshold) / npieces; - double error = NAN; + double error{NAN}; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); double total_alpha_sp = 0.; - gsl_integration_qag(&F_alpha_sp, nu_threshold, nu_max_phixs, 0, CONTINUUM_NU_INTEGRAL_ACCURACY, GSLWSIZE, - GSL_INTEG_GAUSS31, gslworkspace, &total_alpha_sp, &error); + integrator(intparas, nu_threshold, nu_max_phixs, 0, CONTINUUM_NU_INTEGRAL_ACCURACY, + GSL_INTEG_GAUSS31, &total_alpha_sp, &error); double alpha_sp_old = total_alpha_sp; double alpha_sp = total_alpha_sp; @@ -667,8 +581,8 @@ auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel const double xlow = nu_threshold + i * deltanu; // Spontaneous recombination and bf-cooling coefficient don't depend on the cutted radiation field - gsl_integration_qag(&F_alpha_sp, xlow, nu_max_phixs, 0, CONTINUUM_NU_INTEGRAL_ACCURACY, GSLWSIZE, GSL_INTEG_GAUSS31, - gslworkspace, &alpha_sp, &error); + integrator(intparas, xlow, nu_max_phixs, 0, CONTINUUM_NU_INTEGRAL_ACCURACY, + GSL_INTEG_GAUSS31, &alpha_sp, &error); if (zrand >= alpha_sp / total_alpha_sp) { break; @@ -677,7 +591,8 @@ auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel gsl_set_error_handler(previous_handler); - const double nuoffset = (total_alpha_sp * zrand - alpha_sp_old) / (alpha_sp - alpha_sp_old) * deltanu; + const double nuoffset = + (alpha_sp != alpha_sp_old) ? (total_alpha_sp * zrand - alpha_sp_old) / (alpha_sp - alpha_sp_old) * deltanu : 0.; const double nu_lower = nu_threshold + (i - 1) * deltanu + nuoffset; assert_testmodeonly(std::isfinite(nu_lower)); @@ -688,7 +603,7 @@ 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. { - double Alpha_sp = NAN; + double Alpha_sp{NAN}; const int lowerindex = floor(log(T_e / MINTEMP) / T_step_log); assert_always(lowerindex >= 0); if (lowerindex < TABLESIZE - 1) { @@ -721,7 +636,7 @@ auto calculate_ionrecombcoeff(const int modelgridindex, const float T_e, const i double alpha = 0.; if (lowerion < get_nions(element) - 1) { // this gets divided and cancelled out in the radiative case anyway - const double nne = (modelgridindex >= 0) ? grid::get_nne(modelgridindex) : 1.0; + const double nne = (modelgridindex >= 0) ? grid::get_nne(modelgridindex) : 1.; double nnupperion = 0; // nnupperion = get_groundmultiplet_pop(modelgridindex, T_e, element, upperion, assume_lte); @@ -737,12 +652,12 @@ auto calculate_ionrecombcoeff(const int modelgridindex, const float T_e, const i } for (int upper = 0; upper < upper_nlevels; upper++) { - double nnupperlevel = NAN; + double nnupperlevel{NAN}; if (assume_lte) { const double T_exc = T_e; const double E_level = epsilon(element, lowerion + 1, upper); const double E_ground = epsilon(element, lowerion + 1, 0); - const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, lowerion + 1) : 1.0; + const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, lowerion + 1) : 1.; nnupperlevel = (nnground * stat_weight(element, lowerion + 1, upper) / stat_weight(element, lowerion + 1, 0) * exp(-(E_level - E_ground) / KB / T_exc)); @@ -759,12 +674,12 @@ auto calculate_ionrecombcoeff(const int modelgridindex, const float T_e, const i double nnupperlevel_so_far = 0.; const int maxrecombininglevel = get_maxrecombininglevel(element, lowerion + 1); for (int upper = 0; upper <= maxrecombininglevel; upper++) { - double nnupperlevel = NAN; + double nnupperlevel{NAN}; if (assume_lte) { const double T_exc = T_e; const double E_level = epsilon(element, lowerion + 1, upper); const double E_ground = epsilon(element, lowerion + 1, 0); - const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, lowerion + 1) : 1.0; + const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, lowerion + 1) : 1.; nnupperlevel = (nnground * stat_weight(element, lowerion + 1, upper) / stat_weight(element, lowerion + 1, 0) * exp(-(E_level - E_ground) / KB / T_exc)); @@ -840,7 +755,7 @@ static void scale_level_phixs(const int element, const int ion, const int level, static void read_recombrate_file() // calibrate the recombination rates to tabulated values by scaling the photoionisation cross sections { - use_cellhist = false; + use_cellcache = false; FILE *recombrate_file = fopen("recombrates.txt", "r"); if (recombrate_file == nullptr) { printout("No recombrates.txt file found. Skipping recombination rate scaling...\n"); @@ -854,7 +769,7 @@ static void read_recombrate_file() printout("Calibrating recombination rates for a temperature of %.1f K\n", Te_estimate); - struct rrc_row { + struct RRCRow { double log_Te; double rrc_low_n; double rrc_total; @@ -867,12 +782,12 @@ static void read_recombrate_file() while (fscanf(recombrate_file, "%d %d %d\n", &atomicnumber, &upperionstage, &tablerows) > 0) { // printout("%d %d %d\n", atomicnumber, upperionstage, tablerows); - struct rrc_row T_highestbelow = {0, 0, 0}; - struct rrc_row T_lowestabove = {0, 0, 0}; + RRCRow T_highestbelow = {0, 0, 0}; + RRCRow T_lowestabove = {0, 0, 0}; T_highestbelow.log_Te = -1; T_lowestabove.log_Te = -1; for (int i = 0; i < tablerows; i++) { - struct rrc_row row {}; + RRCRow row{}; assert_always(fscanf(recombrate_file, "%lg %lg %lg\n", &row.log_Te, &row.rrc_low_n, &row.rrc_total) == 3); if (row.log_Te < log_Te_estimate && row.log_Te > T_highestbelow.log_Te) { T_highestbelow.log_Te = row.log_Te; @@ -986,8 +901,8 @@ static void precalculate_ion_alpha_sp() { const int nlevels = get_ionisinglevels(element, ion); double zeta = 0.; for (int level = 0; level < nlevels; level++) { - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); - phixstargetindex++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { const double zeta_level = get_spontrecombcoeff(element, ion, level, phixstargetindex, T_e); zeta += zeta_level; } @@ -1006,6 +921,7 @@ void ratecoefficients_init() /// W is easily factored out. For stimulated recombination we must assume /// T_e = T_R for this precalculation. { + printout("time before tabulation of rate coefficients %ld\n", std::time(nullptr)); /// Determine the temperture grids gridsize T_step_log = (log(MAXTEMP) - log(MINTEMP)) / (TABLESIZE - 1.); @@ -1053,6 +969,8 @@ void ratecoefficients_init() read_recombrate_file(); precalculate_ion_alpha_sp(); + + printout("time after tabulation of rate coefficients %ld\n", std::time(nullptr)); } auto interpolate_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, double T) -> double { @@ -1101,11 +1019,6 @@ static auto integrand_stimrecombination_custom_radfield(const double nu, void *v static auto calculate_stimrecombcoeff_integral(int element, int lowerion, int level, int phixstargetindex, int modelgridindex) -> double { - // if (nnlevel <= 1.1 * MINPOP) - // { - // return 0.; - // } - const double epsrel = 1e-3; const double epsabs = 0.; @@ -1114,7 +1027,7 @@ static auto calculate_stimrecombcoeff_integral(int element, int lowerion, int le const double nu_max_phixs = nu_threshold * last_phixs_nuovernuedge; // nu of the uppermost point in the phixs table const auto T_e = grid::get_Te(modelgridindex); - gsl_integral_paras_gammacorr intparas = { + const auto intparas = gsl_integral_paras_gammacorr{ .nu_edge = nu_threshold, .photoion_xs = globals::elements[element].ions[lowerion].levels[level].photoion_xs, .T_e = T_e, @@ -1124,15 +1037,14 @@ static auto calculate_stimrecombcoeff_integral(int element, int lowerion, int le const int upperionlevel = get_phixsupperlevel(element, lowerion, level, phixstargetindex); const double sf = calculate_sahafact(element, lowerion, level, upperionlevel, T_e, H * nu_threshold); - const gsl_function F_stimrecomb = {.function = &integrand_stimrecombination_custom_radfield, .params = &intparas}; - double error = 0.0; + double error = 0.; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); - double stimrecombcoeff = 0.0; + double stimrecombcoeff = 0.; // const int status = - gsl_integration_qag(&F_stimrecomb, nu_threshold, nu_max_phixs, epsabs, epsrel, GSLWSIZE, GSL_INTEG_GAUSS61, - gslworkspace, &stimrecombcoeff, &error); + integrator(intparas, nu_threshold, nu_max_phixs, epsabs, epsrel, + GSL_INTEG_GAUSS61, &stimrecombcoeff, &error); gsl_set_error_handler(previous_handler); @@ -1141,10 +1053,10 @@ static auto calculate_stimrecombcoeff_integral(int element, int lowerion, int le // if (status != 0) // { // error *= FOURPI * get_phixsprobability(element, ion, level, phixstargetindex); - // printout("stimrecombcoeff gsl integrator warning %d. modelgridindex %d Z=%d ionstage %d lower %d phixstargetindex - // %d gamma %g error %g\n", - // status, modelgridindex, get_atomicnumber(element), get_ionstage(element, ion), level, phixstargetindex, - // gammacorr, error); + // printout("stimrecombcoeff gsl integrator warning %d. modelgridindex %d Z=%d ionstage %d lower %d + // phixstargetindex %d gamma %g error %g\n", + // status, modelgridindex, get_atomicnumber(element), get_ionstage(element, ion), level, + // phixstargetindex, gammacorr, error); // } return stimrecombcoeff; @@ -1156,8 +1068,8 @@ auto get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetin { double stimrecombcoeff = -1.; #if (SEPARATE_STIMRECOMB) - if (use_cellhist) { - stimrecombcoeff = globals::cellhistory[tid] + if (use_cellcache) { + stimrecombcoeff = globals::cellcache[cellcacheslotid] .chelements[element] .chions[lowerion] .chlevels[level] @@ -1166,12 +1078,12 @@ auto get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetin } #endif - if (!use_cellhist || stimrecombcoeff < 0) { + if (!use_cellcache || stimrecombcoeff < 0) { stimrecombcoeff = calculate_stimrecombcoeff_integral(element, lowerion, level, phixstargetindex, modelgridindex); #if (SEPARATE_STIMRECOMB) - if (use_cellhist) { - globals::cellhistory[tid] + if (use_cellcache) { + globals::cellcache[cellcacheslotid] .chelements[element] .chions[lowerion] .chlevels[level] @@ -1207,7 +1119,7 @@ static auto integrand_corrphotoioncoeff_custom_radfield(const double nu, void *c const int modelgridindex = params->modelgridindex; #if (SEPARATE_STIMRECOMB) - const double corrfactor = 1.0; + const double corrfactor = 1.; #else const float T_e = params->T_e; double corrfactor = 1. - params->departure_ratio * exp(-HOVERKB * nu / T_e); @@ -1241,20 +1153,16 @@ static auto calculate_corrphotoioncoeff_integral(int element, int ion, int level #else // stimulated recombination is negative photoionisation const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - // if (nnlevel <= 1.1 * MINPOP) - // { - // return 0.; - // } const double nne = grid::get_nne(modelgridindex); const int upperionlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); const double sf = calculate_sahafact(element, ion, level, upperionlevel, T_e, H * nu_threshold); const double nnupperionlevel = get_levelpop(modelgridindex, element, ion + 1, upperionlevel); - double departure_ratio = nnlevel > 0. ? nnupperionlevel / nnlevel * nne * sf : 1.0; // put that to phixslist + double departure_ratio = nnlevel > 0. ? nnupperionlevel / nnlevel * nne * sf : 1.; // put that to phixslist if (!std::isfinite(departure_ratio)) { departure_ratio = 0.; } #endif - gsl_integral_paras_gammacorr intparas = { + const auto intparas = gsl_integral_paras_gammacorr{ .nu_edge = nu_threshold, .departure_ratio = departure_ratio, .photoion_xs = globals::elements[element].ions[ion].levels[level].photoion_xs, @@ -1262,14 +1170,13 @@ static auto calculate_corrphotoioncoeff_integral(int element, int ion, int level .modelgridindex = modelgridindex, }; - const gsl_function F_gammacorr = {.function = &integrand_corrphotoioncoeff_custom_radfield, .params = &intparas}; - double error = 0.0; + double error = 0.; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); - double gammacorr = 0.0; - const int status = gsl_integration_qag(&F_gammacorr, nu_threshold, nu_max_phixs, epsabs, epsrel, GSLWSIZE, - GSL_INTEG_GAUSS61, gslworkspace, &gammacorr, &error); + double gammacorr = 0.; + const int status = integrator( + intparas, nu_threshold, nu_max_phixs, epsabs, epsrel, GSL_INTEG_GAUSS61, &gammacorr, &error); gsl_set_error_handler(previous_handler); @@ -1295,27 +1202,21 @@ auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex /// The correction factor for stimulated emission in gammacorr is set to its /// LTE value. Because the T_e dependence of gammacorr is weak, this correction /// correction may be evaluated at T_R! - double gammacorr = -1; - - 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) { - return gammacorr; + double gammacorr = (use_cellcache) ? globals::cellcache[cellcacheslotid] + .chelements[element] + .chions[ion] + .chlevels[level] + .chphixstargets[phixstargetindex] + .corrphotoioncoeff + : -1; + + if (!use_cellcache || gammacorr < 0) { + 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 (use_cellhist) { - gammacorr = globals::cellhistory[tid] - .chelements[element] - .chions[ion] - .chlevels[level] - .chphixstargets[phixstargetindex] - .corrphotoioncoeff; - } - if (!use_cellhist || gammacorr < 0) { - { + if (!DETAILED_BF_ESTIMATORS_ON || gammacorr < 0) { if constexpr (!USE_LUT_PHOTOION) { gammacorr = calculate_corrphotoioncoeff_integral(element, ion, level, phixstargetindex, modelgridindex); } else { @@ -1327,13 +1228,13 @@ 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[nonemptymgi * get_includedions() + index_in_groundlevelcontestimator]; + gammacorr *= globals::corrphotoionrenorm[nonemptymgi * globals::nbfcontinua_ground + + index_in_groundlevelcontestimator]; } } } - if (use_cellhist) { - globals::cellhistory[tid] + if (use_cellcache) { + globals::cellcache[cellcacheslotid] .chelements[element] .chions[ion] .chlevels[level] @@ -1345,17 +1246,18 @@ auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex return gammacorr; } -static auto get_nlevels_important(int modelgridindex, int element, int ion, bool assume_lte, float T_e, - double *nnlevelsum_out) -> int +static auto get_nlevels_important(const int modelgridindex, const int element, const int ion, const bool assume_lte, + const float T_e) -> std::tuple // get the number of levels that make up a fraction of the ion population // of at least IONGAMMA_POPFRAC_LEVELS_INCLUDED { - if (IONGAMMA_POPFRAC_LEVELS_INCLUDED >= 1.) { - return get_nlevels(element, ion); - } // get the stored ion population for comparison with the cumulative sum of level pops const double nnion_real = get_nnion(modelgridindex, element, ion); + if (IONGAMMA_POPFRAC_LEVELS_INCLUDED >= 1.) { + return {get_nlevels(element, ion), nnion_real}; + } + double nnlevelsum = 0.; int nlevels_important = get_ionisinglevels(element, ion); // levels needed to get majority of ion pop @@ -1366,12 +1268,12 @@ static auto get_nlevels_important(int modelgridindex, int element, int ion, bool for (int lower = 0; (nnlevelsum / nnion_real < IONGAMMA_POPFRAC_LEVELS_INCLUDED) && (lower < get_ionisinglevels(element, ion)); lower++) { - double nnlowerlevel = NAN; + double nnlowerlevel{NAN}; if (assume_lte) { const double T_exc = T_e; // remember, other parts of the code in LTE mode use TJ, not T_e const double E_level = epsilon(element, ion, lower); const double E_ground = epsilon(element, ion, 0); - const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, ion) : 1.0; + const double nnground = (modelgridindex >= 0) ? get_groundlevelpop(modelgridindex, element, ion) : 1.; nnlowerlevel = (nnground * stat_weight(element, ion, lower) / stat_weight(element, ion, 0) * exp(-(E_level - E_ground) / KB / T_exc)); @@ -1381,21 +1283,19 @@ static auto get_nlevels_important(int modelgridindex, int element, int ion, bool nnlevelsum += nnlowerlevel; nlevels_important = lower + 1; } - *nnlevelsum_out = nnlevelsum; assert_always(nlevels_important <= get_nlevels(element, ion)); - // printout("mgi %d element %d ion %d nlevels_important %d popfrac %g\n", modelgridindex, element, ion, - // nlevels_important, nnlevelsum / nnion_real); - return nlevels_important; + return {nlevels_important, nnlevelsum}; } -auto iongamma_is_zero(const int modelgridindex, const int element, const int ion) -> bool { +auto iongamma_is_zero(const int nonemptymgi, const int element, const int ion) -> bool { const int nions = get_nions(element); if (ion >= nions - 1) { return true; } + const int modelgridindex = grid::get_mgi_of_nonemptymgi(nonemptymgi); if constexpr (USE_LUT_PHOTOION) { - return (globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)] == 0); + return (globals::gammaestimator[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)] == 0); } const auto T_e = grid::get_Te(modelgridindex); @@ -1437,8 +1337,7 @@ auto calculate_iongamma_per_gspop(const int modelgridindex, const int element, c const auto T_e = grid::get_Te(modelgridindex); const float nne = grid::get_nne(modelgridindex); - // double nnlowerion = 0.; - // const int nlevels_important = get_nlevels_important(modelgridindex, element, ion, false, T_e, &nnlowerion); + // const auto [nlevels_important, _] = get_nlevels_important(modelgridindex, element, ion, false, T_e); const int nlevels_important = get_nlevels(element, ion); double Col_ion = 0.; @@ -1451,7 +1350,6 @@ auto calculate_iongamma_per_gspop(const int modelgridindex, const int element, c Gamma += nnlevel * get_corrphotoioncoeff(element, ion, level, phixstargetindex, modelgridindex); 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); Col_ion += nnlevel * col_ionization_ratecoeff(T_e, nne, element, ion, level, phixstargetindex, epsilon_trans); } @@ -1472,10 +1370,10 @@ auto calculate_iongamma_per_ionpop(const int modelgridindex, const float T_e, co assert_always(lowerion < get_nions(element) - 1); assert_always(!force_bfest || !force_bfintegral); - const float nne = (modelgridindex >= 0) ? grid::get_nne(modelgridindex) : 1.0; + const float nne = (modelgridindex >= 0) ? grid::get_nne(modelgridindex) : 1.; - double nnlowerion = 0.; - const int nlevels_important = get_nlevels_important(modelgridindex, element, lowerion, assume_lte, T_e, &nnlowerion); + const auto [nlevels_important, nnlowerion] = + get_nlevels_important(modelgridindex, element, lowerion, assume_lte, T_e); if (nnlowerion <= 0.) { return 0.; @@ -1484,7 +1382,7 @@ auto calculate_iongamma_per_ionpop(const int modelgridindex, const float T_e, co double gamma_ion = 0.; double gamma_ion_used = 0.; for (int lower = 0; lower < nlevels_important; lower++) { - double nnlowerlevel = NAN; + double nnlowerlevel{NAN}; if (assume_lte) { const double T_exc = T_e; const double E_level = epsilon(element, lowerion, lower); @@ -1517,10 +1415,10 @@ auto calculate_iongamma_per_ionpop(const int modelgridindex, const float T_e, co } if (force_bfintegral || printdebug) { - // use the cellhistory but not the detailed bf estimators + // use the cellcache but not the detailed bf estimators gamma_coeff_integral += calculate_corrphotoioncoeff_integral(element, lowerion, lower, phixstargetindex, modelgridindex); - // double gamma_coeff_integral_level_ch = globals::cellhistory[tid] + // double gamma_coeff_integral_level_ch = globals::cellcache[cellcacheslotid] // .chelements[element] // .chions[lowerion] // .chlevels[lower] diff --git a/ratecoeff.h b/ratecoeff.h index 46eaeb44e..14abd1ccc 100644 --- a/ratecoeff.h +++ b/ratecoeff.h @@ -1,130 +1,94 @@ +#pragma once #ifndef RATECOEFF_H #define RATECOEFF_H -void ratecoefficients_init(); - -void setup_photoion_luts(); - -auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel, float T_e) -> double; - -auto interpolate_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, double T) -> double; - -auto get_spontrecombcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double; -auto get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetindex, int modelgridindex) -> double; - -auto get_bfcoolingcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double; - -auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, int modelgridindex) -> double; -auto get_corrphotoioncoeff_ana(int element, int ion, int level, int phixstargetindex, int modelgridindex) -> double; - -auto iongamma_is_zero(int modelgridindex, int element, int ion) -> bool; - -auto calculate_iongamma_per_gspop(int modelgridindex, int element, int ion) -> double; -auto 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, - bool force_bfintegral) -> double; - -auto calculate_ionrecombcoeff(int modelgridindex, float T_e, int element, int upperion, bool assume_lte, - bool collisional_not_radiative, bool printdebug, bool lower_superlevel_only, - bool per_groundmultipletpop, bool stimonly) -> double; - -// CUDA integration. might be worth revisting for SYCL support -// template -// __global__ void kernel_simpson_integral(void *intparas, double xlow, double deltax, int samplecount, double -// *integral) -// /// Integrand to calculate the rate coefficient for photoionization -// /// using gsl integrators. Corrected for stimulated recombination. -// { -// extern __shared__ double threadcontrib[]; - -// const int i = blockIdx.x * blockDim.x + threadIdx.x; +#include -// if (i < samplecount) { -// // Simpson's rule integral (will later be divided by 3) -// // n must be odd -// // integral = (xn - x0) / 3 * {f(x_0) + 4 * f(x_1) + 2 * f(x_2) + ... + 4 * f(x_1) + f(x_n-1)} -// // weights e.g., 1,4,2,4,2,4,1 -// double weight; -// if (i == 0 || i == (samplecount - 1)) { -// weight = 1.; -// } else if (i % 2 == 0) { -// weight = 2.; -// } else { -// weight = 4.; -// } +#include "sn3d.h" +#if !USE_SIMPSON_INTEGRATOR +#include +#include -// const double x = xlow + deltax * i; +#include "constants.h" +#endif -// threadcontrib[threadIdx.x] = weight * func_integrand(x, intparas) * deltax; -// } else { -// threadcontrib[threadIdx.x] = 0.; -// } - -// __syncthreads(); - -// if (threadIdx.x == 0) { -// double blockcontrib = threadcontrib[0]; -// for (int x = 1; x < blockDim.x; x++) { -// blockcontrib += threadcontrib[x]; -// } -// atomicAdd(integral, blockcontrib / 3.); // divided by 3 for Simpson rule -// } -// } - -// template -// double calculate_integral_gpu(T intparas, double xlow, double xhigh) { -// T *dev_intparas; -// checkCudaErrors(cudaMalloc(&dev_intparas, sizeof(T))); - -// #ifdef __CUDA_ARCH__ -// memcpy(dev_intparas, &intparas, sizeof(T)); -// // *dev_intparas = intparas; -// #else -// checkCudaErrors(cudaMemcpy(dev_intparas, &intparas, sizeof(T), cudaMemcpyHostToDevice)); -// #endif - -// double *dev_integral; -// cudaMalloc(&dev_integral, sizeof(double)); - -// #ifdef __CUDA_ARCH__ -// *dev_integral = 0.; -// #else -// cudaMemset(dev_integral, 0, sizeof(double)); -// #endif - -// checkCudaErrors(cudaDeviceSynchronize()); - -// const int samplecount = globals::NPHIXSPOINTS * 16 + 1; // need an odd number for Simpson rule -// assert_always(samplecount % 2 == 1); - -// dim3 threadsPerBlock(32, 1, 1); -// dim3 numBlocks((samplecount + threadsPerBlock.x - 1) / threadsPerBlock.x, 1, 1); -// size_t sharedsize = sizeof(double) * threadsPerBlock.x; +void ratecoefficients_init(); -// const double deltax = (xhigh - xlow) / samplecount; +void setup_photoion_luts(); -// kernel_simpson_integral -// <<>>((void *)dev_intparas, xlow, deltax, samplecount, dev_integral); - -// // Check for any errors launching the kernel -// checkCudaErrors(cudaGetLastError()); - -// // cudaDeviceSynchronize waits for the kernel to finish, and returns any errors encountered during the launch. -// checkCudaErrors(cudaDeviceSynchronize()); - -// #ifdef __CUDA_ARCH__ -// const double result = *dev_integral; -// #else -// double result = 0.; -// checkCudaErrors(cudaMemcpy(&result, dev_integral, sizeof(double), cudaMemcpyDeviceToHost)); -// #endif - -// cudaFree(dev_integral); -// cudaFree(dev_intparas); - -// return result; -// } - -extern double T_step_log; +[[nodiscard]] auto select_continuum_nu(int element, int lowerion, int lower, int upperionlevel, float T_e) -> double; + +[[nodiscard]] auto interpolate_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, + double T) -> double; + +[[nodiscard]] auto get_spontrecombcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double; +[[nodiscard]] auto get_stimrecombcoeff(int element, int lowerion, int level, int phixstargetindex, + int modelgridindex) -> double; + +[[nodiscard]] auto get_bfcoolingcoeff(int element, int ion, int level, int phixstargetindex, float T_e) -> double; + +[[nodiscard]] auto get_corrphotoioncoeff(int element, int ion, int level, int phixstargetindex, + int modelgridindex) -> double; +[[nodiscard]] auto get_corrphotoioncoeff_ana(int element, int ion, int level, int phixstargetindex, + int modelgridindex) -> double; + +[[nodiscard]] auto iongamma_is_zero(int nonemptymgi, int element, int ion) -> bool; + +[[nodiscard]] auto calculate_iongamma_per_gspop(int modelgridindex, int element, int ion) -> double; +[[nodiscard]] auto 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, bool force_bfintegral) -> double; + +[[nodiscard]] auto calculate_ionrecombcoeff(int modelgridindex, float T_e, int element, int upperion, bool assume_lte, + bool collisional_not_radiative, bool printdebug, bool lower_superlevel_only, + bool per_groundmultipletpop, bool stimonly) -> double; + +inline double T_step_log{}; + +template +constexpr auto simpson_integrator(auto ¶ms, double a, double b, int samplecount) -> double { + assert_testmodeonly(samplecount % 2 == 1); + + const double deltax = (b - a) / samplecount; + + double integral = 0.; + for (int i = 0; i < samplecount; i++) { + // Simpson's rule integral (will later be divided by 3) + // n must be odd + // integral = (xn - x0) / 3 * {f(x_0) + 4 * f(x_1) + 2 * f(x_2) + ... + 4 * f(x_1) + f(x_n-1)} + // weights e.g., 1,4,2,4,2,4,1 + double weight{1.}; + if (i == 0 || i == (samplecount - 1)) { + weight = 1.; + } else if (i % 2 == 0) { + weight = 2.; + } else { + weight = 4.; + } + + const double x = a + deltax * i; + + integral += weight * func_integrand(x, ¶ms) * deltax; + } + integral /= 3.; + + return integral; +} + +template +auto integrator(auto params, double a, double b, double epsabs, double epsrel, int key, double *result, + double *abserr) { + if constexpr (USE_SIMPSON_INTEGRATOR) { + // need an odd number for Simpson rule + const int samplecount = std::max(1, static_cast((b / a) / globals::NPHIXSNUINCREMENT)) * 4 + 1; + + *result = simpson_integrator(params, a, b, samplecount); + *abserr = 0.; + return 0; + } else { + const gsl_function F = {.function = (func_integrand), .params = &(params)}; + return gsl_integration_qag(&F, a, b, epsabs, epsrel, GSLWSIZE, key, gslworkspace.get(), result, abserr); + } +} #endif // RATECOEFF_H \ No newline at end of file diff --git a/rpkt.cc b/rpkt.cc index 6292aab64..f1b68c029 100644 --- a/rpkt.cc +++ b/rpkt.cc @@ -1,25 +1,98 @@ #include "rpkt.h" +#ifdef MPI_ON +#include +#endif + #include +#include #include +#include +#include +#include +#include #include #include +#include +#include #include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "globals.h" #include "grid.h" #include "ltepop.h" +#include "macroatom.h" +#include "packet.h" #include "radfield.h" #include "sn3d.h" #include "stats.h" #include "vectors.h" #include "vpkt.h" -// Material for handing r-packet propagation. +namespace { + +constexpr float expopac_lambdamin = 534.5; +constexpr float expopac_lambdamax = 35000.; +constexpr float expopac_deltalambda = 35.5; +constexpr auto expopac_nbins = + static_cast((expopac_lambdamax - expopac_lambdamin) / expopac_deltalambda); + +// kappa in cm^2/g for each bin of each non-empty cell +std::span expansionopacities{}; + +// kappa times Planck function for each bin of each non-empty cell +std::span expansionopacity_planck_cumulative{}; +#ifdef MPI_ON +MPI_Win win_expansionopacities = MPI_WIN_NULL; +MPI_Win win_expansionopacity_planck_cumulative = MPI_WIN_NULL; +#endif + +} // anonymous namespace + +void allocate_expansionopacities() { + if constexpr (!EXPANSIONOPACITIES_ON) { + return; + } + + const auto npts_nonempty = grid::get_nonempty_npts_model(); + float *expansionopacities_data{}; + double *expansionopacity_planck_cumulative_data{}; +#ifdef MPI_ON + int my_rank_nonemptycells = npts_nonempty / globals::node_nprocs; + // rank_in_node 0 gets any remainder + if (globals::rank_in_node == 0) { + my_rank_nonemptycells += npts_nonempty - (my_rank_nonemptycells * globals::node_nprocs); + } + MPI_Aint size = my_rank_nonemptycells * expopac_nbins * static_cast(sizeof(float)); + int disp_unit = sizeof(float); + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, + &expansionopacities_data, &win_expansionopacities) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win_expansionopacities, 0, &size, &disp_unit, &expansionopacities_data) == + MPI_SUCCESS); + + if constexpr (EXPANSION_OPAC_SAMPLE_KAPPAPLANCK) { + MPI_Aint size = my_rank_nonemptycells * expopac_nbins * static_cast(sizeof(double)); + int disp_unit = sizeof(double); + assert_always(MPI_Win_allocate_shared(size, disp_unit, MPI_INFO_NULL, globals::mpi_comm_node, + &expansionopacity_planck_cumulative_data, + &win_expansionopacity_planck_cumulative) == MPI_SUCCESS); + assert_always(MPI_Win_shared_query(win_expansionopacity_planck_cumulative, 0, &size, &disp_unit, + &expansionopacity_planck_cumulative_data) == MPI_SUCCESS); + } -constexpr int RPKT_EVENTTYPE_BB = 550; -constexpr int RPKT_EVENTTYPE_CONT = 551; +#else + expansionopacities_data = static_cast(malloc(npts_nonempty * expopac_nbins * sizeof(float))); + if constexpr (EXPANSION_OPAC_SAMPLE_KAPPAPLANCK) { + expansionopacity_planck_cumulative_data = + static_cast(malloc(npts_nonempty * expopac_nbins * sizeof(double))); + } +#endif + expansionopacities = std::span{expansionopacities_data, static_cast(npts_nonempty * expopac_nbins)}; + expansionopacity_planck_cumulative = std::span{ + expansionopacity_planck_cumulative_data, + static_cast(expansionopacity_planck_cumulative_data == nullptr ? 0 : npts_nonempty * expopac_nbins)}; +} auto closest_transition(const double nu_cmf, const int next_trans) -> int /// for the propagation through non empty cells @@ -36,8 +109,8 @@ auto closest_transition(const double nu_cmf, const int next_trans) -> int return -1; } - if (next_trans > 0) { - /// if left = pkt_ptr->next_trans > 0 we know the next line we should interact with, independent of the packets + if (next_trans > 0) [[likely]] { + /// if left = pkt.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 next_trans; } @@ -47,40 +120,143 @@ auto closest_transition(const double nu_cmf, const int next_trans) -> int std::lower_bound(globals::linelist, 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) { + if (matchindex >= globals::nlines) [[unlikely]] { return -1; } return matchindex; } +static auto get_nu_cmf_abort(const std::array pos, const std::array dir, const double prop_time, + const double nu_rf, const double abort_dist) -> double { + // get the frequency change per distance travelled assuming linear change to the abort distance + // this is done is two parts to get identical results to do_rpkt_step() + const auto half_abort_dist = abort_dist / 2.; + const auto abort_time = prop_time + half_abort_dist / CLIGHT_PROP + half_abort_dist / CLIGHT_PROP; + + const std::array abort_pos{pos[0] + (dir[0] * half_abort_dist) + (dir[0] * half_abort_dist), + pos[1] + (dir[1] * half_abort_dist) + (dir[1] * half_abort_dist), + pos[2] + (dir[2] * half_abort_dist) + (dir[2] * half_abort_dist)}; + + const double nu_cmf_abort = nu_rf * doppler_packet_nucmf_on_nurf(abort_pos, dir, abort_time); + + return nu_cmf_abort; +} + +// wavelength bins are ordered by ascending wavelength (descending frequency) + +static constexpr auto get_expopac_bin_nu_upper(const size_t binindex) -> double { + const auto lambda_lower = expopac_lambdamin + binindex * expopac_deltalambda; + return 1e8 * CLIGHT / lambda_lower; +} + +static constexpr auto get_expopac_bin_nu_lower(const size_t binindex) -> double { + const auto lambda_upper = expopac_lambdamin + (binindex + 1) * expopac_deltalambda; + return 1e8 * CLIGHT / lambda_upper; +} + +static auto get_event_expansion_opacity( + const int modelgridindex, const int nonemptymgi, const Packet &pkt, + Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, // NOLINT(misc-unused-parameters) + Phixslist &phixslist, const double tau_rnd, const double abort_dist) -> std::tuple { + calculate_chi_rpkt_cont(pkt.nu_cmf, chi_rpkt_cont, &phixslist, modelgridindex); + const auto doppler = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + + const auto nu_cmf_abort = get_nu_cmf_abort(pkt.pos, pkt.dir, pkt.prop_time, pkt.nu_rf, abort_dist); + + // for USE_RELATIVISTIC_DOPPLER_SHIFT, we will use a linear approximation for + // the frequency change from start to abort (cell boundary/timestep end) + const auto d_nu_on_d_l = (nu_cmf_abort - pkt.nu_cmf) / abort_dist; + + auto pos = pkt.pos; + auto nu_rf = pkt.nu_rf; + auto nu_cmf = pkt.nu_cmf; + auto e_rf = pkt.e_rf; + auto e_cmf = pkt.e_cmf; + auto prop_time = pkt.prop_time; + assert_always(globals::cellcache[cellcacheslotid].cellnumber == modelgridindex); + double dist = 0.; + double tau = 0.; + auto binindex_start = static_cast(((1e8 * CLIGHT / nu_cmf) - expopac_lambdamin) / expopac_deltalambda); + if (binindex_start < 0) { + binindex_start = 0; + } + + for (ptrdiff_t binindex = binindex_start; binindex < expopac_nbins; binindex++) { + const auto next_bin_edge_nu = (binindex < 0) ? get_expopac_bin_nu_upper(0) : get_expopac_bin_nu_lower(binindex); + const auto binedgedist = get_linedistance(prop_time, nu_cmf, next_bin_edge_nu, d_nu_on_d_l); + + const double chi_cont = chi_rpkt_cont.total * doppler; + // const auto chi_cont = 0.; + double chi_bb_expansionopac = 0.; + if (binindex >= 0) { + const auto kappa = expansionopacities[nonemptymgi * expopac_nbins + binindex]; + // const auto doppler = doppler_packet_nucmf_on_nurf(dummypkt.pos, dummypkt.dir, dummypkt.prop_time); + // const auto doppler = (pkt.nu_cmf + d_nu_on_d_l * dist) / pkt.nu_rf; + chi_bb_expansionopac = kappa * grid::get_rho(modelgridindex) * doppler; + } + + const double chi_tot = chi_cont + chi_bb_expansionopac; + + if (chi_tot * binedgedist > tau_rnd - tau) { + // event occurs + const auto edist = std::max(dist + (tau_rnd - tau) / chi_tot, 0.); + const bool event_is_boundbound = rng_uniform() <= chi_bb_expansionopac / chi_tot; + return {edist, event_is_boundbound}; + } + + tau += chi_tot * binedgedist; + dist += binedgedist; + + if constexpr (!USE_RELATIVISTIC_DOPPLER_SHIFT) { + move_pkt_withtime(pos, pkt.dir, prop_time, nu_rf, nu_cmf, e_rf, e_cmf, binedgedist); + } else { + // avoid move_pkt_withtime() to skip the standard Doppler shift calculation + // and use the linear approx instead + + pos[0] += (pkt.dir[0] * binedgedist); + pos[1] += (pkt.dir[1] * binedgedist); + pos[2] += (pkt.dir[2] * binedgedist); + prop_time += binedgedist / CLIGHT_PROP; + nu_cmf = pkt.nu_cmf + d_nu_on_d_l * dist; // should equal nu_trans; + assert_testmodeonly(nu_cmf <= pkt.nu_cmf); + } + + if (nu_cmf <= nu_cmf_abort) { + // hit edge of cell or timestep limit + return {std::numeric_limits::max(), false}; + } + } + // no more bins, so no opacity and no chance of further interaction below this frequency + return {std::numeric_limits::max(), false}; +} + static auto get_event(const int modelgridindex, - struct packet *pkt_ptr, // pointer to packet object - int *rpkt_eventtype, + const Packet &pkt, // pointer to packet object + const Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, MacroAtomState &mastate, const double tau_rnd, // random optical depth until which the packet travels const double abort_dist // maximal travel distance before packet leaves cell or time step ends - ) -> double -// returns edist, the distance to the next physical event (continuum or bound-bound) -// BE AWARE THAT THIS PROCEDURE SHOULD BE ONLY CALLED FOR NON EMPTY CELLS!! + ) -> std::tuple +// returns edist, the distance to the next physical event (continuum or bound-bound) and is_boundbound_event, a +// boolean BE AWARE THAT THIS PROCEDURE SHOULD BE ONLY CALLED FOR NON EMPTY CELLS!! { + assert_testmodeonly(!EXPANSIONOPACITIES_ON); + assert_testmodeonly(grid::modelgrid[modelgridindex].thick != 1); // printout("get_event()\n"); /// initialize loop variables - struct packet dummypkt_abort = *pkt_ptr; - // this is done is two parts to get identical results to do_rpkt_step() - move_pkt_withtime(&dummypkt_abort, abort_dist / 2.); - move_pkt_withtime(&dummypkt_abort, abort_dist / 2.); - const double nu_cmf_abort = dummypkt_abort.nu_cmf; - assert_testmodeonly(nu_cmf_abort <= pkt_ptr->nu_cmf); - // for USE_RELATIVISTIC_DOPPLER_SHIFT, we will use a linear approximation for - // the frequency change from start to abort (cell boundary/timestep end) - const double d_nu_on_d_l = (nu_cmf_abort - pkt_ptr->nu_cmf) / abort_dist; + const auto nu_cmf_abort = get_nu_cmf_abort(pkt.pos, pkt.dir, pkt.prop_time, pkt.nu_rf, abort_dist); + const auto d_nu_on_d_l = (nu_cmf_abort - pkt.nu_cmf) / abort_dist; - struct packet dummypkt = *pkt_ptr; + auto pos = pkt.pos; + auto nu_rf = pkt.nu_rf; + auto nu_cmf = pkt.nu_cmf; + auto e_rf = pkt.e_rf; + auto e_cmf = pkt.e_cmf; + auto prop_time = pkt.prop_time; + int next_trans = pkt.next_trans; - 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); + const double chi_cont = chi_rpkt_cont.total * doppler_packet_nucmf_on_nurf(pos, pkt.dir, prop_time); double tau = 0.; // optical depth along path double dist = 0.; // position on path while (true) { @@ -88,9 +264,9 @@ static auto get_event(const int modelgridindex, /// 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.nu_cmf, - dummypkt.next_trans); /// returns negative value if nu_cmf > nu_trans - if (lineindex >= 0) { + + /// returns negative value if nu_cmf > nu_trans + if (const int lineindex = closest_transition(nu_cmf, next_trans); lineindex >= 0) [[likely]] { /// line interaction is possible (nu_cmf > nu_trans) const double nu_trans = globals::linelist[lineindex].nu; @@ -98,20 +274,19 @@ static auto get_event(const int modelgridindex, // 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 packet in a single line - dummypkt.next_trans = lineindex + 1; + next_trans = lineindex + 1; - const double ldist = get_linedistance(dummypkt.prop_time, dummypkt.nu_cmf, nu_trans, d_nu_on_d_l); + const double ldist = get_linedistance(prop_time, nu_cmf, nu_trans, d_nu_on_d_l); 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 (nu_trans < nu_cmf_abort) { + if (nu_trans < nu_cmf_abort) [[unlikely]] { // 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(); + return {std::numeric_limits::max(), next_trans - 1, false}; } const int element = globals::linelist[lineindex].elementindex; @@ -125,7 +300,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); - const double tau_line = std::max(0., (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * dummypkt.prop_time); + const double tau_line = std::max(0., (B_lu * n_l - B_ul * n_u) * HCLIGHTOVERFOURPI * prop_time); // printout("[debug] get_event: tau_line %g\n", tau_line); // printout("[debug] get_event: tau_rnd - tau > tau_cont\n"); @@ -141,70 +316,57 @@ static auto get_event(const int modelgridindex, tau += tau_cont + tau_line; if constexpr (!USE_RELATIVISTIC_DOPPLER_SHIFT) { - move_pkt_withtime(&dummypkt, ldist); + move_pkt_withtime(pos, pkt.dir, prop_time, nu_rf, nu_cmf, e_rf, e_cmf, ldist); } else { // avoid move_pkt_withtime() to skip the standard Doppler shift calculation // and use the linear approx instead - 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); + pos[0] += (pkt.dir[0] * ldist); + pos[1] += (pkt.dir[1] * ldist); + pos[2] += (pkt.dir[2] * ldist); + prop_time += ldist / CLIGHT_PROP; + nu_cmf = pkt.nu_cmf + d_nu_on_d_l * dist; // should equal nu_trans; + assert_testmodeonly(nu_cmf <= pkt.nu_cmf); } - radfield::update_lineestimator(modelgridindex, lineindex, - dummypkt.prop_time * CLIGHT * dummypkt.e_cmf / dummypkt.nu_cmf); + radfield::update_lineestimator(modelgridindex, lineindex, prop_time * CLIGHT * e_cmf / nu_cmf); } else { /// bound-bound process occurs // printout("[debug] get_event: tau_rnd - tau <= tau_cont + tau_line: bb-process occurs\n"); - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion; - pkt_ptr->mastate.level = upper; /// if the MA will be activated it must be in the transitions upper level - pkt_ptr->mastate.activatingline = lineindex; + mastate.element = element; + mastate.ion = ion; + mastate.level = upper; /// if the MA will be activated it must be in the transitions upper level + mastate.activatingline = lineindex; 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); + move_pkt_withtime(pos, pkt.dir, prop_time, nu_rf, nu_cmf, e_rf, e_cmf, ldist); + radfield::update_lineestimator(modelgridindex, lineindex, prop_time * CLIGHT * e_cmf / nu_cmf); } - *rpkt_eventtype = RPKT_EVENTTYPE_BB; /// the line and its parameters were already selected by closest_transition! // 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.next_trans; - - return dist + ldist; + return {dist + ldist, next_trans, true}; } } else { /// continuum process occurs before reaching the line - *rpkt_eventtype = RPKT_EVENTTYPE_CONT; - - pkt_ptr->next_trans = dummypkt.next_trans - 1; - - return dist + (tau_rnd - tau) / chi_cont; + return {dist + (tau_rnd - tau) / chi_cont, next_trans - 1, false}; } - } else { + } else [[unlikely]] { /// no line interaction possible - check whether continuum process occurs in cell const double tau_cont = chi_cont * (abort_dist - dist); if (tau_rnd - tau > tau_cont) { // no continuum event before abort_dist - return std::numeric_limits::max(); + return {std::numeric_limits::max(), next_trans, false}; } /// continuum process occurs at edist - *rpkt_eventtype = RPKT_EVENTTYPE_CONT; - - pkt_ptr->next_trans = globals::nlines + 1; - - return dist + (tau_rnd - tau) / chi_cont; + return {dist + (tau_rnd - tau) / chi_cont, globals::nlines + 1, false}; } } @@ -212,21 +374,19 @@ static auto get_event(const int modelgridindex, assert_always(false); } -static void electron_scatter_rpkt(struct packet *pkt_ptr) { +static void electron_scatter_rpkt(Packet &pkt) { /// now make the packet a r-pkt and set further flags - pkt_ptr->type = TYPE_RPKT; - pkt_ptr->last_cross = BOUNDARY_NONE; /// allow all further cell crossings + pkt.type = TYPE_RPKT; + pkt.last_cross = BOUNDARY_NONE; /// allow all further cell crossings - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); + const auto vel_vec = get_velocity(pkt.pos, pkt.prop_time); // Transform Stokes Parameters from the RF to the CMF - double Qi = pkt_ptr->stokes[1]; - double Ui = pkt_ptr->stokes[2]; + double Qi = pkt.stokes[1]; + double Ui = pkt.stokes[2]; - double old_dir_cmf[3]; - frame_transform(pkt_ptr->dir, &Qi, &Ui, vel_vec, old_dir_cmf); + const auto old_dir_cmf = frame_transform(pkt.dir, &Qi, &Ui, vel_vec); // Outcoming direction. Compute the new cmf direction from the old direction and the scattering angles (see Kalos & // Whitlock 2008) @@ -237,15 +397,13 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { if constexpr (DIPOLE) { // Assume dipole function (rejecton method, see Code & Whitney 1995) double p = 0.; - double x = 0.; - do { + double x = 1.; + while (x > p) { const double zrand = rng_uniform(); - const double zrand2 = rng_uniform(); - const double zrand3 = rng_uniform(); M = 2 * zrand - 1; mu = pow(M, 2.); - phisc = 2 * PI * zrand2; + phisc = 2 * PI * rng_uniform(); // NB: the rotational matrix R here is chosen in the clockwise direction ("+"). // In Bulla+2015 equation (10) and (12) refer to the specific case shown in Fig.2 where the angle i2 @@ -256,20 +414,19 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { p = (mu + 1) + (mu - 1) * (cos(2 * phisc) * Qi + sin(2 * phisc) * Ui); // generate a number between 0 and the maximum of the previous function (2) - x = 2 * zrand3; - } while (x > p); + x = 2 * rng_uniform(); + }; } else { // Assume isotropic scattering const double zrand = rng_uniform(); - const double zrand2 = rng_uniform(); M = 2. * zrand - 1; mu = pow(M, 2.); - phisc = 2 * PI * zrand2; + phisc = 2 * PI * rng_uniform(); } const double tsc = acos(M); - double new_dir_cmf[3]; + std::array new_dir_cmf{}; if (fabs(old_dir_cmf[2]) < 0.99999) { new_dir_cmf[0] = sin(tsc) / sqrt(1. - pow(old_dir_cmf[2], 2.)) * @@ -291,15 +448,13 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { // Need to rotate Stokes Parameters in the scattering plane - double ref1[3]; - double ref2[3]; - meridian(old_dir_cmf, ref1, ref2); + auto [ref1_olddir, ref2_olddir] = meridian(old_dir_cmf); // 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 i1 = rot_angle(old_dir_cmf, new_dir_cmf, ref1_olddir, ref2_olddir); const double cos2i1 = cos(2 * i1); const double sin2i1 = sin(2 * i1); @@ -311,16 +466,12 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { mu = dot(old_dir_cmf, new_dir_cmf); 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; - const double I = 1.0; // Inew / Inew + const double Qnew = (0.75 * ((mu * mu - 1.0) + Qold * (mu * mu + 1.0))) / Inew; + const double Unew = (1.5 * mu * Uold) / Inew; // Need to rotate Stokes Parameters out of the scattering plane to the meridian frame (Clockwise rotation of PI-i2) - meridian(new_dir_cmf, ref1, ref2); + auto [ref1, ref2] = meridian(new_dir_cmf); // 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 @@ -333,103 +484,86 @@ static void electron_scatter_rpkt(struct packet *pkt_ptr) { double U = -Qnew * sin2i2 + Unew * cos2i2; // Transform Stokes Parameters from the CMF to the RF - double vel_rev[3]; - vel_rev[0] = -vel_vec[0]; - vel_rev[1] = -vel_vec[1]; - vel_rev[2] = -vel_vec[2]; - - 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 + pkt.dir = frame_transform(new_dir_cmf, &Q, &U, std::array{-vel_vec[0], -vel_vec[1], -vel_vec[2]}); - pkt_ptr->dir[0] = dummy_dir[0]; - pkt_ptr->dir[1] = dummy_dir[1]; - pkt_ptr->dir[2] = dummy_dir[2]; + pkt.stokes = {1., Q, U}; // Check unit vector - assert_testmodeonly(fabs(vec_len(pkt_ptr->dir) - 1.) < 1.e-6); + assert_testmodeonly(fabs(vec_len(pkt.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->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; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.nu_rf = pkt.nu_cmf / dopplerfactor; + pkt.e_rf = pkt.e_cmf / dopplerfactor; } -static void rpkt_event_continuum(struct packet *pkt_ptr, - const struct rpkt_continuum_absorptioncoeffs &chi_rpkt_cont_thisthread, - int modelgridindex) { - const double nu = pkt_ptr->nu_cmf; +static void rpkt_event_continuum(Packet &pkt, const Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, + const Phixslist &phixslist, const int modelgridindex) { + const double nu = pkt.nu_cmf; - 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; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + const double chi_cont = chi_rpkt_cont.total * dopplerfactor; + const double chi_escatter = chi_rpkt_cont.ffescat * dopplerfactor; + const double chi_ff = chi_rpkt_cont.ffheat * dopplerfactor; + const double chi_bf = chi_rpkt_cont.bf * dopplerfactor; /// 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*chi_cont %g, sigma %g, chi_ff %g, chi_bf %g\n", zrand * chi_cont, // sigma, chi_ff, chi_bf); - if (zrand * chi_cont < sigma) { + const auto chi_rnd = rng_uniform() * chi_cont; + + if (chi_rnd < chi_escatter) { /// electron scattering occurs - /// in this case the packet stays a R_PKT of same nu_cmf than before (coherent scattering) + /// in this case the packet stays a R_PKT of same nu_cmf as before (coherent scattering) /// but with different direction // printout("[debug] rpkt_event: electron scattering\n"); - pkt_ptr->interactions += 1; - pkt_ptr->nscatterings += 1; - pkt_ptr->last_event = 12; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.nscatterings += 1; + pkt.last_event = LASTEVENT_ELECTRONSCATTERING; stats::increment(stats::COUNTER_ESCOUNTER); // generate a virtual packet - vpkt_call_estimators(pkt_ptr, TYPE_RPKT); + vpkt_call_estimators(pkt, TYPE_RPKT); + + // pkt.nu_cmf = 3.7474058e+14; + electron_scatter_rpkt(pkt); - // pkt_ptr->nu_cmf = 3.7474058e+14; - electron_scatter_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 - vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); - pkt_ptr->em_time = pkt_ptr->prop_time; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; - /// 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 * chi_cont < sigma + chi_ff) { + } else if (chi_rnd < chi_escatter + chi_ff) { /// ff: transform to k-pkt // printout("[debug] rpkt_event: free-free transition\n"); stats::increment(stats::COUNTER_K_STAT_FROM_FF); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 5; - pkt_ptr->type = TYPE_KPKT; - pkt_ptr->absorptiontype = -1; - } else if (zrand * chi_cont < sigma + chi_ff + chi_bf) { + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 5; + pkt.type = TYPE_KPKT; + pkt.absorptiontype = -1; + } else if (chi_rnd < chi_escatter + 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; + pkt.absorptiontype = -2; - const double chi_bf_inrest = chi_rpkt_cont_thisthread.bf; - assert_always(globals::phixslist[tid].chi_bf_sum[globals::nbfcontinua - 1] == chi_bf_inrest); + const double chi_bf_inrest = chi_rpkt_cont.bf; + assert_always(phixslist.chi_bf_sum[phixslist.allcontend - 1] == chi_bf_inrest); /// Determine in which continuum the bf-absorption occurs - const double zrand2 = rng_uniform(); - const double chi_bf_rand = zrand2 * chi_bf_inrest; + const double chi_bf_rand = rng_uniform() * chi_bf_inrest; // 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 auto *upperval = std::upper_bound(phixslist.chi_bf_sum.data() + phixslist.allcontbegin, + phixslist.chi_bf_sum.data() + phixslist.allcontend - 1, chi_bf_rand); + const int allcontindex = std::distance(phixslist.chi_bf_sum.data(), upperval); + assert_always(allcontindex < phixslist.allcontend); const double nu_edge = globals::allcont[allcontindex].nu_edge; const int element = globals::allcont[allcontindex].element; @@ -441,44 +575,39 @@ static void rpkt_event_continuum(struct packet *pkt_ptr, // 1, 0, ion, level); printout("[debug] rpkt_event: bound-free: nu_edge %g, nu %g\n", nu_edge, nu); if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats_contabsorption(pkt_ptr, modelgridindex, element, ion); + stats::increment_ion_stats_contabsorption(pkt, modelgridindex, element, ion); } /// and decide whether we go to ionisation energy - const double zrand3 = rng_uniform(); - if (zrand3 < nu_edge / nu) { + if (rng_uniform() < nu_edge / nu) { stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_BF); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 3; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 3; if constexpr (TRACK_ION_STATS) { - stats::increment_ion_stats(modelgridindex, element, ion + 1, stats::ION_MACROATOM_ENERGYIN_PHOTOION, - pkt_ptr->e_cmf); + stats::increment_ion_stats(modelgridindex, element, ion + 1, stats::ION_MACROATOM_ENERGYIN_PHOTOION, pkt.e_cmf); } - pkt_ptr->type = TYPE_MA; - pkt_ptr->mastate.element = element; - pkt_ptr->mastate.ion = ion + 1; + pkt.type = TYPE_MA; const int upper = get_phixsupperlevel(element, ion, level, phixstargetindex); - pkt_ptr->mastate.level = upper; - pkt_ptr->mastate.activatingline = -99; + + do_macroatom(pkt, {element, ion + 1, upper, -99}); } /// or to the thermal pool else { /// transform to k-pkt // printout("[debug] rpkt_event: bound-free: transform to k-pkt\n"); stats::increment(stats::COUNTER_K_STAT_FROM_BF); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 4; - pkt_ptr->type = TYPE_KPKT; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 4; + pkt.type = TYPE_KPKT; } } else { - printout("ERROR: could not continuum process\n"); - abort(); + assert_always(false); } } -static void rpkt_event_boundbound(struct packet *pkt_ptr, const int mgi) { +static void rpkt_event_boundbound(Packet &pkt, MacroAtomState &pktmastate, const int mgi) { /// bound-bound transition occured /// activate macro-atom in corresponding upper-level. Actually all the information /// about the macro atoms state has already been set by closest_transition, so @@ -486,178 +615,189 @@ static void rpkt_event_boundbound(struct packet *pkt_ptr, const int mgi) { // printout("[debug] rpkt_event: bound-bound activation of macroatom\n"); // if (tid == 0) ma_stat_activation_bb++; stats::increment(stats::COUNTER_MA_STAT_ACTIVATION_BB); - pkt_ptr->interactions += 1; - pkt_ptr->last_event = 1; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.last_event = 1; - pkt_ptr->absorptiontype = pkt_ptr->mastate.activatingline; - pkt_ptr->absorptionfreq = pkt_ptr->nu_rf; // pkt_ptr->nu_cmf; - pkt_ptr->absorptiondir[0] = pkt_ptr->dir[0]; - pkt_ptr->absorptiondir[1] = pkt_ptr->dir[1]; - pkt_ptr->absorptiondir[2] = pkt_ptr->dir[2]; - pkt_ptr->type = TYPE_MA; + pkt.absorptiontype = pktmastate.activatingline; + pkt.absorptionfreq = pkt.nu_rf; + pkt.absorptiondir[0] = pkt.dir[0]; + pkt.absorptiondir[1] = pkt.dir[1]; + pkt.absorptiondir[2] = pkt.dir[2]; + pkt.type = TYPE_MA; if constexpr (TRACK_ION_STATS) { - const int element = pkt_ptr->mastate.element; - const int ion = pkt_ptr->mastate.ion; - stats::increment_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_RADEXC, pkt_ptr->e_cmf); + const int element = pktmastate.element; + const int ion = pktmastate.ion; + stats::increment_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_RADEXC, pkt.e_cmf); - const int et = pkt_ptr->emissiontype; + const int et = pkt.emissiontype; if (et >= 0) { const int emissionelement = globals::linelist[et].elementindex; const int emissionion = globals::linelist[et].ionindex; stats::increment_ion_stats(mgi, emissionelement, emissionion, stats::ION_BOUNDBOUND_ABSORBED, - pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf); + pkt.e_cmf / H / pkt.nu_cmf); } } if constexpr (RECORD_LINESTAT) { - safeincrement(globals::acounter[pkt_ptr->next_trans - 1]); + atomicadd(globals::acounter[pkt.next_trans - 1], 1); } + + do_macroatom(pkt, pktmastate); } -static void rpkt_event_thickcell(struct packet *pkt_ptr) +auto sample_planck_times_expansion_opacity(const int nonemptymgi) -> double +// returns a randomly chosen frequency with a distribution of Planck function times the expansion opacity +{ + assert_testmodeonly(EXPANSION_OPAC_SAMPLE_KAPPAPLANCK); + + const auto *kappa_planck_bins = &expansionopacity_planck_cumulative[nonemptymgi * expopac_nbins]; + + const auto rnd_integral = rng_uniform() * kappa_planck_bins[expopac_nbins - 1]; + const auto *selected_partintegral = + std::upper_bound(kappa_planck_bins, kappa_planck_bins + expopac_nbins, rnd_integral); + const auto binindex = std::min(std::distance(kappa_planck_bins, selected_partintegral), expopac_nbins - 1); + assert_testmodeonly(binindex >= 0); + assert_testmodeonly(binindex < expopac_nbins); + + // use a linear interpolation for the frequency within the bin + const auto bin_nu_lower = get_expopac_bin_nu_lower(binindex); + const auto delta_nu = get_expopac_bin_nu_upper(binindex) - bin_nu_lower; + const double nuoffset = rng_uniform() * delta_nu; + const double nu = bin_nu_lower + nuoffset; + return nu; +} + +static void rpkt_event_thickcell(Packet &pkt) /// Event handling for optically thick cells. Those cells are treated in a grey /// approximation with electron scattering only. /// The packet stays an R_PKT of same nu_cmf than before (coherent scattering) /// but with different direction. { // printout("[debug] rpkt_event_thickcell: electron scattering\n"); - pkt_ptr->interactions += 1; - pkt_ptr->nscatterings += 1; - pkt_ptr->last_event = 12; + stats::increment(stats::COUNTER_INTERACTIONS); + pkt.nscatterings += 1; + pkt.last_event = LASTEVENT_ELECTRONSCATTERING; stats::increment(stats::COUNTER_ESCOUNTER); - emit_rpkt(pkt_ptr); + emit_rpkt(pkt); /// 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 - vec_copy(pkt_ptr->em_pos, pkt_ptr->pos); - pkt_ptr->em_time = pkt_ptr->prop_time; + pkt.em_pos = pkt.pos; + pkt.em_time = pkt.prop_time; } -static void update_estimators(const struct packet *pkt_ptr, const double distance, const int modelgridindex) +static void update_estimators(const double e_cmf, const double nu_cmf, const double distance, + const double doppler_nucmf_on_nurf, const int nonemptymgi, + const Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, const Phixslist &phixslist, + const bool thickcell) /// 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. { /// Update only non-empty cells - if (modelgridindex == grid::get_npts_model()) { + assert_testmodeonly(nonemptymgi >= 0); + const double distance_e_cmf = distance * e_cmf; + + radfield::update_estimators(nonemptymgi, distance_e_cmf, nu_cmf, doppler_nucmf_on_nurf, phixslist, thickcell); + + if (thickcell) { + // chi_rpkt_cont and phixslist are not known for thick cells return; } - const double distance_e_cmf = distance * pkt_ptr->e_cmf; - const double nu = pkt_ptr->nu_cmf; - const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); - radfield::update_estimators(nonemptymgi, distance_e_cmf, nu, pkt_ptr); /// 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::chi_rpkt_cont[tid].ffheating); + atomicadd(globals::ffheatingestimator[nonemptymgi], distance_e_cmf * chi_rpkt_cont.ffheating); if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - const double distance_e_cmf_over_nu = distance_e_cmf / nu; - for (int i = 0; i < globals::nbfcontinua_ground; i++) { const double nu_edge = globals::groundcont[i].nu_edge; - 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_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; - const int ionestimindex = get_ionestimindex_nonemptymgi(nonemptymgi, element, ion); - - if constexpr (USE_LUT_PHOTOION) { - safeadd(globals::gammaestimator[ionestimindex], - globals::phixslist[tid].groundcont_gamma_contr[i] * distance_e_cmf_over_nu); - - if (!std::isfinite(globals::gammaestimator[ionestimindex])) { - printout( - "[fatal] update_estimators: gamma estimator becomes non finite: mgi %d element %d ion %d gamma_contr " - "%g, distance_e_cmf_over_nu %g\n", - modelgridindex, element, ion, globals::phixslist[tid].groundcont_gamma_contr[i], - distance_e_cmf_over_nu); - abort(); - } - } + if (nu_cmf <= nu_edge) { + // because groundcont is sorted by nu_edge descending, nu < nu_edge for all remaining items + return; + } + const int ionestimindex = nonemptymgi * globals::nbfcontinua_ground + i; - if constexpr (USE_LUT_BFHEATING) { - safeadd(globals::bfheatingestimator[ionestimindex], - globals::phixslist[tid].groundcont_gamma_contr[i] * distance_e_cmf * (1. - nu_edge / nu)); - } - } - } else { - break; // because groundcont is sorted by nu_edge descending, nu < nu_edge for all remaining items + if constexpr (USE_LUT_PHOTOION) { + atomicadd(globals::gammaestimator[ionestimindex], + phixslist.groundcont_gamma_contr[i] * (distance_e_cmf / nu_cmf)); + } + + if constexpr (USE_LUT_BFHEATING) { + atomicadd(globals::bfheatingestimator[ionestimindex], + phixslist.groundcont_gamma_contr[i] * distance_e_cmf * (1. - nu_edge / nu_cmf)); } } } } -static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool -// Routine for moving an r-packet. Similar to do_gamma in objective. -// return value - true if no mgi change, no pkttype change and not reached end of timestep, false otherwise +static auto do_rpkt_step(Packet &pkt, const double t2) -> bool +// Update an r-packet and return true if no mgi change (or it goes into an empty cell) and no pkttype change and not +// reached end of timestep, otherwise false { - const int cellindex = pkt_ptr->where; - int mgi = grid::get_cell_modelgridindex(cellindex); - const int oldmgi = mgi; - - // if (pkt_ptr->next_trans > 0) { - // printout("[debug] do_rpkt: init: pkt_ptr->nu_cmf %g, nu(pkt_ptr->next_trans=%d) - // %g, nu(pkt_ptr->next_trans-1=%d) %g, pkt_ptr->where %d\n", pkt_ptr->nu_cmf, pkt_ptr->next_trans, - // globals::linelist[pkt_ptr->next_trans].nu, pkt_ptr->next_trans-1, globals::linelist[pkt_ptr->next_trans-1].nu, - // pkt_ptr->where ); - // } - - // Assign optical depth to next physical event. And start counter of - // optical depth for this path. + const int cellindex = pkt.where; + const int mgi = grid::get_cell_modelgridindex(cellindex); + const int nonemptymgi = (mgi != grid::get_npts_model()) ? grid::get_modelcell_nonemptymgi(mgi) : -1; + + static thread_local struct MacroAtomState pktmastate {}; + static thread_local struct Phixslist phixslist { + .groundcont_gamma_contr = std::vector(globals::nbfcontinua_ground, 0.), + .chi_bf_sum = std::vector(globals::nbfcontinua, 0.), + .gamma_contr = std::vector(globals::bfestimcount, 0.), .allcontend = 1, .allcontbegin = 0, .bfestimend = 1, + .bfestimbegin = 0, + }; + + static thread_local struct Rpkt_continuum_absorptioncoeffs chi_rpkt_cont { + .nu = NAN, .total = NAN, .ffescat = NAN, .ffheat = NAN, .bf = NAN, .modelgridindex = -1, .timestep = -1 + }; + + // Assign optical depth to next physical event const double zrand = rng_uniform_pos(); const double tau_next = -1. * log(zrand); // Start by finding the distance to the crossing of the grid cell // boundaries. sdist is the boundary distance and snext is the // grid cell into which we pass. - int snext = 0; - double sdist = grid::boundary_distance(pkt_ptr->dir, pkt_ptr->pos, pkt_ptr->prop_time, pkt_ptr->where, &snext, - &pkt_ptr->last_cross); + auto [sdist, snext] = grid::boundary_distance(pkt.dir, pkt.pos, pkt.prop_time, pkt.where, &pkt.last_cross); if (sdist == 0) { - grid::change_cell(pkt_ptr, snext); - const int cellindexnew = pkt_ptr->where; - mgi = grid::get_cell_modelgridindex(cellindexnew); + grid::change_cell(pkt, snext); + const int cellindexnew = pkt.where; + const int newmgi = grid::get_cell_modelgridindex(cellindexnew); - return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); + return (pkt.type == TYPE_RPKT && (newmgi == grid::get_npts_model() || newmgi == mgi)); } 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; + ? globals::rmax * pkt.prop_time / globals::tmin + : 2 * globals::rmax * (pkt.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); - abort(); + printout("[fatal] do_rpkt: Unreasonably large sdist for packet %d. Rpkt. Abort. %g %g %g\n", pkt.number, + globals::rmax, pkt.prop_time / globals::tmin, sdist); + std::abort(); } if (sdist < 0) { - const int cellindexnew = pkt_ptr->where; + const int cellindexnew = pkt.where; printout("[warning] r_pkt: Negative distance (sdist = %g). Abort.\n", sdist); printout("[warning] r_pkt: cell %d snext %d\n", cellindexnew, snext); - printout("[warning] r_pkt: pos %g %g %g\n", pkt_ptr->pos[0], pkt_ptr->pos[1], pkt_ptr->pos[2]); - printout("[warning] r_pkt: dir %g %g %g\n", pkt_ptr->dir[0], pkt_ptr->dir[1], pkt_ptr->dir[2]); + printout("[warning] r_pkt: pos %g %g %g\n", pkt.pos[0], pkt.pos[1], pkt.pos[2]); + printout("[warning] r_pkt: dir %g %g %g\n", pkt.dir[0], pkt.dir[1], pkt.dir[2]); printout("[warning] r_pkt: cell corner %g %g %g\n", - 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(cellindexnew, 0) * pkt_ptr->prop_time / globals::tmin); + grid::get_cellcoordmin(cellindexnew, 0) * pkt.prop_time / globals::tmin, + grid::get_cellcoordmin(cellindexnew, 1) * pkt.prop_time / globals::tmin, + grid::get_cellcoordmin(cellindexnew, 2) * pkt.prop_time / globals::tmin); + printout("[warning] r_pkt: cell width %g\n", grid::wid_init(cellindexnew, 0) * pkt.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"); - printout("[fatal] r_pkt: Current cell %d, target cell %d.\n", pkt_ptr->where, snext); - abort(); + printout("[fatal] r_pkt: Current cell %d, target cell %d.\n", pkt.where, snext); + std::abort(); } if (sdist > globals::max_path_step) { sdist = globals::max_path_step; - snext = pkt_ptr->where; + snext = pkt.where; } // At present there is no scattering/destruction process so all that needs to @@ -665,175 +805,205 @@ static auto do_rpkt_step(struct packet *pkt_ptr, const double t2) -> bool // Find how far it can travel during the time inverval. - const double tdist = (t2 - pkt_ptr->prop_time) * CLIGHT_PROP; + const double tdist = (t2 - pkt.prop_time) * CLIGHT_PROP; assert_always(tdist >= 0); + const double abort_dist = std::min(tdist, sdist); + /// Get distance to the next physical event (continuum or bound-bound) double edist = -1; - int rpkt_eventtype = -1; - if (mgi == grid::get_npts_model()) { + bool event_is_boundbound = true; + const bool thickcell = grid::modelgrid[mgi].thick == 1; + if (nonemptymgi < 0) { /// for empty cells no physical event occurs. The packets just propagate. edist = std::numeric_limits::max(); - 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) { + pkt.next_trans = -1; // skip over lines and search for line list position on the next non-empty cell + } else if (thickcell) [[unlikely]] { /// In the case of optically thick cells, we treat the packets in grey approximation to speed up the calculation - 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; - pkt_ptr->next_trans = -1; + const double chi_grey = + grid::get_kappagrey(mgi) * grid::get_rho(mgi) * doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + + edist = tau_next / chi_grey; + pkt.next_trans = -1; + } else if constexpr (EXPANSIONOPACITIES_ON) { + std::tie(edist, event_is_boundbound) = + get_event_expansion_opacity(mgi, nonemptymgi, pkt, chi_rpkt_cont, phixslist, tau_next, abort_dist); + pkt.next_trans = -1; } else { - edist = get_event(mgi, pkt_ptr, &rpkt_eventtype, tau_next, fmin(tdist, sdist)); + calculate_chi_rpkt_cont(pkt.nu_cmf, chi_rpkt_cont, &phixslist, mgi); + + std::tie(edist, pkt.next_trans, event_is_boundbound) = + get_event(mgi, pkt, chi_rpkt_cont, pktmastate, tau_next, abort_dist); } assert_always(edist >= 0); - if ((sdist < tdist) && (sdist < edist)) { + if ((sdist <= tdist) && (sdist <= edist)) { // Move it into the new cell. - move_pkt_withtime(pkt_ptr, sdist / 2.); - update_estimators(pkt_ptr, sdist, mgi); - move_pkt_withtime(pkt_ptr, sdist / 2.); - - if (snext != pkt_ptr->where) { - grid::change_cell(pkt_ptr, snext); - const int cellindexnew = pkt_ptr->where; - mgi = grid::get_cell_modelgridindex(cellindexnew); + const double doppler_nucmf_on_nurf = move_pkt_withtime(pkt, sdist / 2.); + if (nonemptymgi >= 0) { + update_estimators(pkt.e_cmf, pkt.nu_cmf, sdist, doppler_nucmf_on_nurf, nonemptymgi, chi_rpkt_cont, phixslist, + thickcell); + } + move_pkt_withtime(pkt, sdist / 2.); + + int newmgi = mgi; + if (snext != pkt.where) { + grid::change_cell(pkt, snext); + const int cellindexnew = pkt.where; + newmgi = grid::get_cell_modelgridindex(cellindexnew); } - pkt_ptr->last_event = pkt_ptr->last_event + 100; + pkt.last_event = pkt.last_event + 100; - return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); + return (pkt.type == TYPE_RPKT && (newmgi == grid::get_npts_model() || newmgi == mgi)); } - if ((edist < sdist) && (edist < tdist)) { + if ((edist <= sdist) && (edist <= tdist)) [[likely]] { // bound-bound or continuum event - move_pkt_withtime(pkt_ptr, edist / 2.); - update_estimators(pkt_ptr, edist, mgi); - move_pkt_withtime(pkt_ptr, edist / 2.); - - // The previously selected and in pkt_ptr stored event occurs. Handling is done by rpkt_event - if (grid::modelgrid[mgi].thick == 1) { - rpkt_event_thickcell(pkt_ptr); - } 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::chi_rpkt_cont[tid], mgi); + const double doppler_nucmf_on_nurf = move_pkt_withtime(pkt, edist / 2.); + update_estimators(pkt.e_cmf, pkt.nu_cmf, edist, doppler_nucmf_on_nurf, nonemptymgi, chi_rpkt_cont, phixslist, + thickcell); + move_pkt_withtime(pkt, edist / 2.); + + // The previously selected and in pkt stored event occurs. Handling is done by rpkt_event + if (thickcell) { + rpkt_event_thickcell(pkt); + } else if (event_is_boundbound) { + if constexpr (EXPANSIONOPACITIES_ON) { + if constexpr (EXPANSION_OPAC_SAMPLE_KAPPAPLANCK) { + pkt.nu_cmf = sample_planck_times_expansion_opacity(nonemptymgi); + } + rpkt_event_thickcell(pkt); + } else { + rpkt_event_boundbound(pkt, pktmastate, mgi); + } } else { - assert_always(false); + rpkt_event_continuum(pkt, chi_rpkt_cont, phixslist, mgi); } - return (pkt_ptr->type == TYPE_RPKT && (mgi == grid::get_npts_model() || mgi == oldmgi)); + return (pkt.type == TYPE_RPKT); } - if ((tdist < sdist) && (tdist < edist)) { + if ((tdist <= sdist) && (tdist <= edist)) [[unlikely]] { // reaches end of timestep before cell boundary or interaction - move_pkt_withtime(pkt_ptr, tdist / 2.); - update_estimators(pkt_ptr, tdist, mgi); - pkt_ptr->prop_time = t2; - move_pkt(pkt_ptr, tdist / 2.); - pkt_ptr->last_event = pkt_ptr->last_event + 1000; + const double doppler_nucmf_on_nurf = move_pkt_withtime(pkt, tdist / 2.); + if (nonemptymgi >= 0) { + update_estimators(pkt.e_cmf, pkt.nu_cmf, tdist, doppler_nucmf_on_nurf, nonemptymgi, chi_rpkt_cont, phixslist, + thickcell); + } + move_pkt_withtime(pkt, tdist / 2.); + pkt.prop_time = t2; + pkt.last_event = pkt.last_event + 1000; 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); - abort(); + printout("[fatal] do_rpkt: Trouble was due to packet number %d.\n", pkt.number); + std::abort(); } -void do_rpkt(struct packet *pkt_ptr, const double t2) { - while (do_rpkt_step(pkt_ptr, t2)) { +void do_rpkt(Packet &pkt, const double t2) { + while (do_rpkt_step(pkt, t2)) { ; } } -void emit_rpkt(struct packet *pkt_ptr) { +void emit_rpkt(Packet &pkt) { /// now make the packet a r-pkt and set further flags - pkt_ptr->type = TYPE_RPKT; - pkt_ptr->last_cross = BOUNDARY_NONE; /// allow all further cell crossings + pkt.type = TYPE_RPKT; + pkt.last_cross = BOUNDARY_NONE; /// allow all further cell crossings /// Need to assign a new direction. Assume isotropic emission in the cmf - double dir_cmf[3]; - get_rand_isotropic_unitvec(dir_cmf); + const auto dir_cmf = get_rand_isotropic_unitvec(); - double vel_vec[3]; /// This direction is in the cmf - we want to convert it to the rest /// frame - use aberation of angles. We want to convert from cmf to /// rest so need -ve velocity. - get_velocity(pkt_ptr->pos, vel_vec, -1. * pkt_ptr->prop_time); + const auto vel_vec = get_velocity(pkt.pos, -1. * pkt.prop_time); /// negative time since we want the backwards transformation here - angle_ab(dir_cmf, vel_vec, pkt_ptr->dir); - // printout("[debug] pkt_ptr->dir in RF: %g %g %g\n",pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); + pkt.dir = angle_ab(dir_cmf, vel_vec); + // printout("[debug] pkt.dir in RF: %g %g %g\n",pkt.dir[0],pkt.dir[1],pkt.dir[2]); /// 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->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; + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pkt.pos, pkt.dir, pkt.prop_time); + pkt.nu_rf = pkt.nu_cmf / dopplerfactor; + pkt.e_rf = pkt.e_cmf / dopplerfactor; // Reset polarization information - pkt_ptr->stokes[0] = 1.; - pkt_ptr->stokes[1] = 0.; - pkt_ptr->stokes[2] = 0.; + pkt.stokes[0] = 1.; + pkt.stokes[1] = 0.; + pkt.stokes[2] = 0.; - std::array dummy_dir = {0., 0., 1.}; - cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); + pkt.pol_dir = cross_prod(pkt.dir, std::array{0., 0., 1.}); - if ((dot(pkt_ptr->pol_dir, pkt_ptr->pol_dir)) < 1.e-8) { - dummy_dir = {0., 0., 1.}; - cross_prod(pkt_ptr->dir, dummy_dir, pkt_ptr->pol_dir); + if ((dot(pkt.pol_dir, pkt.pol_dir)) < 1.e-8) { + pkt.pol_dir = cross_prod(pkt.dir, std::array{0., 1., 0.}); } - vec_norm(pkt_ptr->pol_dir, pkt_ptr->pol_dir); - // printout("initialise pol state of packet %g, %g, %g, %g, - // %g\n",pkt_ptr->stokes_qu[0],pkt_ptr->stokes_qu[1],pkt_ptr->pol_dir[0],pkt_ptr->pol_dir[1],pkt_ptr->pol_dir[2]); - // printout("pkt direction %g, %g, %g\n",pkt_ptr->dir[0],pkt_ptr->dir[1],pkt_ptr->dir[2]); + pkt.pol_dir = vec_norm(pkt.pol_dir); } -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.); +static auto calculate_chi_ffheat_nnionpart(const int modelgridindex) -> double { const double g_ff = 1; - - const auto nne = grid::get_nne(modelgridindex); - const auto T_e = grid::get_Te(modelgridindex); - - double chi_ff = 0.; - // chi_ffheating = 0.; + double chi_ff_nnionpart = 0.; const int nelements = get_nelements(); for (int element = 0; element < nelements; element++) { - for (int ion = 0; ion < get_nions(element); ion++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { const double nnion = get_nnion(modelgridindex, element, ion); const int ioncharge = get_ionstage(element, ion) - 1; - chi_ff += ioncharge * ioncharge * g_ff * nnion; + chi_ff_nnionpart += ioncharge * ioncharge * g_ff * nnion; } } - chi_ff *= 3.69255e8 / sqrt(T_e) * pow(nu, -3) * nne * (1 - exp(-HOVERKB * nu / T_e)); + const auto T_e = grid::get_Te(modelgridindex); + + return chi_ff_nnionpart * 3.69255e8 / sqrt(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(); +static auto get_chi_ff_nnionpart(const int modelgridindex) -> double { + if (!use_cellcache || globals::cellcache[cellcacheslotid].cellnumber != modelgridindex) { + return calculate_chi_ffheat_nnionpart(modelgridindex); } + if (globals::cellcache[cellcacheslotid].chi_ff_nnionpart < 0.) { + globals::cellcache[cellcacheslotid].chi_ff_nnionpart = calculate_chi_ffheat_nnionpart(modelgridindex); + } + + return globals::cellcache[cellcacheslotid].chi_ff_nnionpart; +} + +static auto calculate_chi_ffheating(const int modelgridindex, const double nu) -> double +// calculate the free-free absorption (to kpkt heating) coefficient [cm^-1] +// = kappa(free-free) * nne +{ + assert_always(nu > 0.); + + const auto nne = grid::get_nne(modelgridindex); + const auto T_e = grid::get_Te(modelgridindex); + const double chi_ff = get_chi_ff_nnionpart(modelgridindex) * pow(nu, -3) * nne * (1 - exp(-HOVERKB * nu / T_e)); + + assert_testmodeonly(std::isfinite(chi_ff)); + return chi_ff; } -template -auto calculate_chi_bf_gammacontr(const int modelgridindex, const double nu) -> double +template +static auto calculate_chi_bf_gammacontr(const int modelgridindex, const double nu, Phixslist *phixslist) -> double // bound-free opacity { + assert_always(!USECELLHISTANDUPDATEPHIXSLIST || phixslist != nullptr); + 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.; - } + if constexpr (USECELLHISTANDUPDATEPHIXSLIST && (USE_LUT_PHOTOION || USE_LUT_BFHEATING)) { + std::ranges::fill(phixslist->groundcont_gamma_contr, 0.); } const auto T_e = grid::get_Te(modelgridindex); @@ -845,47 +1015,66 @@ auto calculate_chi_bf_gammacontr(const int modelgridindex, const double nu) -> d /// is possible, so set their kappas to zero // break the list into nu >= nu_edge and the remainder (nu < nu_edge) - // first element i such that nu < nu_edge[i] - // const int lastindex = std::upper_bound(globals::allcont_nu_edge, globals::allcont_nu_edge + globals::nbfcontinua, - // nu, - // [](const double &nu, const double &nu_edge) { return nu < nu_edge; }) - - // &globals::allcont_nu_edge[0]; int i = 0; - const int nbfcontinua = globals::nbfcontinua; - for (i = 0; i < nbfcontinua; i++) { + const int allcontend = + std::distance(globals::allcont_nu_edge.cbegin(), + std::upper_bound(globals::allcont_nu_edge.cbegin(), globals::allcont_nu_edge.cend(), nu)); + + const int allcontbegin = std::distance( + globals::allcont_nu_edge.data(), + std::lower_bound( + globals::allcont_nu_edge.data(), globals::allcont_nu_edge.data() + allcontend, nu, + [](const double nu_edge, const double nu_cmf) { return nu_edge * last_phixs_nuovernuedge < nu_cmf; })); + + assert_testmodeonly(allcontbegin >= 0); + assert_testmodeonly(allcontend <= globals::nbfcontinua); + assert_testmodeonly(allcontbegin <= allcontend); + + if constexpr (USECELLHISTANDUPDATEPHIXSLIST) { + phixslist->allcontbegin = allcontbegin; + phixslist->allcontend = allcontend; + + phixslist->bfestimend = + std::distance(globals::bfestim_nu_edge.cbegin(), + std::upper_bound(globals::bfestim_nu_edge.cbegin(), globals::bfestim_nu_edge.cend(), nu)); + + phixslist->bfestimbegin = std::distance( + globals::bfestim_nu_edge.data(), + std::lower_bound( + globals::bfestim_nu_edge.data(), globals::bfestim_nu_edge.data() + phixslist->bfestimend, nu, + [](const double nu_edge, const double nu_cmf) { return nu_edge * last_phixs_nuovernuedge < nu_cmf; })); + } + + for (i = allcontbegin; i < allcontend; i++) { const int element = globals::allcont[i].element; const int ion = globals::allcont[i].ion; const int level = globals::allcont[i].level; + const auto bfestimindex = globals::allcont[i].bfestimindex; /// The bf process happens only if the current cell contains /// the involved atomic species if ((DETAILED_BF_ESTIMATORS_ON && grid::get_elem_abundance(modelgridindex, element) > 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; - 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 + (!DETAILED_BF_ESTIMATORS_ON && ((get_nnion(modelgridindex, element, ion) / nnetot > 1.e-6) || (level == 0)))) + [[likely]] { + const double nnlevel = USECELLHISTANDUPDATEPHIXSLIST ? get_levelpop(modelgridindex, element, ion, level) + : calculate_levelpop(modelgridindex, element, ion, level); - if (nu <= nu_max_phixs && nnlevel > 0) { + if (nnlevel > 0) { + const double nu_edge = globals::allcont[i].nu_edge; const double sigma_bf = photoionization_crosssection_fromtable(globals::allcont[i].photoion_xs, nu_edge, nu); - const double probability = globals::allcont[i].probability; - 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 (!usecellhistupdatephixslist || departure_ratio < 0) { + double departure_ratio = globals::cellcache[cellcacheslotid].ch_allcont_departureratios[i]; + if (!USECELLHISTANDUPDATEPHIXSLIST || departure_ratio < 0) { const int upper = globals::allcont[i].upperlevel; - const double nnupperionlevel = usecellhistupdatephixslist + const double nnupperionlevel = USECELLHISTANDUPDATEPHIXSLIST ? 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 - if (usecellhistupdatephixslist) { - globals::cellhistory[tid].ch_allcont_departureratios[i] = departure_ratio; + if (USECELLHISTANDUPDATEPHIXSLIST) { + globals::cellcache[cellcacheslotid].ch_allcont_departureratios[i] = departure_ratio; } } @@ -893,145 +1082,189 @@ auto calculate_chi_bf_gammacontr(const int modelgridindex, const double nu) -> d corrfactor = std::max(0., 1 - stimfactor); // photoionisation minus stimulated recombination } - const double sigma_contr = sigma_bf * probability * corrfactor; + const double sigma_contr = sigma_bf * globals::allcont[i].probability * corrfactor; - 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_contr; + if constexpr (USECELLHISTANDUPDATEPHIXSLIST && (USE_LUT_PHOTOION || USE_LUT_BFHEATING)) { + if (level == 0 && globals::allcont[i].phixstargetindex == 0) { + phixslist->groundcont_gamma_contr[globals::allcont[i].index_in_groundphixslist] = sigma_contr; } } - if constexpr (usecellhistupdatephixslist && DETAILED_BF_ESTIMATORS_ON) { - globals::phixslist[tid].gamma_contr[i] = sigma_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, - grid::get_elem_abundance(modelgridindex, element)); - printout("[fatal] nne %g, nnlevel %g, (or %g)\n", grid::get_nne(modelgridindex), nnlevel, - get_levelpop(modelgridindex, element, ion, level)); - printout("[fatal] sigma_bf %g, T_e %g, nu %g, nu_edge %g\n", sigma_bf, grid::get_Te(modelgridindex), nu, - nu_edge); - abort(); + if constexpr (USECELLHISTANDUPDATEPHIXSLIST && DETAILED_BF_ESTIMATORS_ON) { + if (bfestimindex >= 0) { + phixslist->gamma_contr[bfestimindex] = sigma_contr; + } } - chi_bf_sum += chi_bf_contr; - if constexpr (usecellhistupdatephixslist) { - globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; + chi_bf_sum += nnlevel * sigma_contr; + if constexpr (USECELLHISTANDUPDATEPHIXSLIST) { + phixslist->chi_bf_sum[i] = chi_bf_sum; } - } else if constexpr (usecellhistupdatephixslist) { + } else if constexpr (USECELLHISTANDUPDATEPHIXSLIST) { // ignore this particular process - globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; + phixslist->chi_bf_sum[i] = chi_bf_sum; if constexpr (DETAILED_BF_ESTIMATORS_ON) { - globals::phixslist[tid].gamma_contr[i] = 0.; + if (bfestimindex >= 0) { + phixslist->gamma_contr[bfestimindex] = 0.; + } } } - } else if constexpr (usecellhistupdatephixslist) { + } else if constexpr (USECELLHISTANDUPDATEPHIXSLIST) { // no element present or not an important level - globals::phixslist[tid].chi_bf_sum[i] = chi_bf_sum; + phixslist->chi_bf_sum[i] = chi_bf_sum; if constexpr (DETAILED_BF_ESTIMATORS_ON) { - globals::phixslist[tid].gamma_contr[i] = 0.; + if (bfestimindex >= 0) { + phixslist->gamma_contr[bfestimindex] = 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.; - } - } - } + assert_always(std::isfinite(chi_bf_sum)); return chi_bf_sum; } -void calculate_chi_rpkt_cont(const double nu_cmf, struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont_thisthread, - const int modelgridindex, const bool usecellhistupdatephixslist) { +void calculate_chi_rpkt_cont(const double nu_cmf, Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, Phixslist *phixslist, + const int modelgridindex) { 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)) { + if ((modelgridindex == chi_rpkt_cont.modelgridindex) && (globals::timestep == chi_rpkt_cont.timestep) && + (fabs(chi_rpkt_cont.nu / nu_cmf - 1.0) < 1e-4)) { // calculated values are a match already return; } const auto nne = grid::get_nne(modelgridindex); - double sigma = 0.0; - double chi_ff = 0.; + double chi_escat = 0.; + /// free-free absorption + const double chi_ff = calculate_chi_ffheating(modelgridindex, nu_cmf); double chi_bf = 0.; double chi_ffheating = 0.; - if (globals::opacity_case == 4) { + if (globals::opacity_case >= 4) { /// First contribution: Thomson scattering on free electrons - sigma = SIGMA_T * nne; - // reduced e/s for debugging - // sigma = 1e-30*sigma; - // switched off e/s for debugging - // sigma_cmf = 0. * nne; - // sigma *= 0.1; + chi_escat = SIGMA_T * nne; - /// Second contribution: free-free absorption - chi_ff = calculate_chi_ff(modelgridindex, nu_cmf); chi_ffheating = chi_ff; /// Third contribution: bound-free absorption - 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 chi_bf %g \n", pkt_lambda, chi_bf); - // } + chi_bf = (phixslist != nullptr) ? calculate_chi_bf_gammacontr(modelgridindex, nu_cmf, phixslist) + : calculate_chi_bf_gammacontr(modelgridindex, nu_cmf, phixslist); + } else { /// in the other cases chi_grey is an mass absorption coefficient /// therefore use the mass density - // sigma = globals::cell[pkt_ptr->where].chi_grey * globals::cell[pkt_ptr->where].rho; + // sigma = globals::cell[pkt.where].chi_grey * globals::cell[pkt.where].rho; // sigma = SIGMA_T * nne; - sigma = 0.; + chi_escat = 0.; // chi_ff = 0.9*sigma; // sigma *= 0.1; - // chi_bf = 0.; - - // Second contribution: free-free absorption - chi_ff = 1e5 * calculate_chi_ff(modelgridindex, nu_cmf); chi_bf = 0.; } - 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.nu = nu_cmf; + chi_rpkt_cont.modelgridindex = modelgridindex; + chi_rpkt_cont.timestep = globals::timestep; + chi_rpkt_cont.total = chi_escat + chi_bf + chi_ff; + chi_rpkt_cont.ffescat = chi_escat; + chi_rpkt_cont.ffheat = chi_ff; + chi_rpkt_cont.bf = chi_bf; + chi_rpkt_cont.ffheating = chi_ffheating; // chi_rpkt_cont_thisthread.bfheating = chi_bfheating; - if (!std::isfinite(chi_rpkt_cont_thisthread->total)) { + if (!std::isfinite(chi_rpkt_cont.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] es %g, ff %g, bf %g\n", chi_rpkt_cont.ffescat, chi_rpkt_cont.ffheat, chi_rpkt_cont.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", 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; + printout("[fatal] pkt.nu_cmf %g\n", nu_cmf); + if (std::isfinite(chi_rpkt_cont.ffescat)) { + chi_rpkt_cont.ffheat = 0.; + chi_rpkt_cont.bf = 0.; + chi_rpkt_cont.total = chi_rpkt_cont.ffescat; } else { - abort(); + std::abort(); } } } + +#ifdef MPI_ON +void MPI_Bcast_binned_opacities(const int modelgridindex, const int root_node_id) { + if constexpr (EXPANSIONOPACITIES_ON) { + if (globals::rank_in_node == 0) { + const auto nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + assert_always(nonemptymgi >= 0); + MPI_Bcast(&expansionopacities[nonemptymgi * expopac_nbins], expopac_nbins, MPI_FLOAT, root_node_id, + globals::mpi_comm_internode); + + if constexpr (EXPANSION_OPAC_SAMPLE_KAPPAPLANCK) { + MPI_Bcast(&expansionopacity_planck_cumulative[nonemptymgi * expopac_nbins], expopac_nbins, MPI_DOUBLE, + root_node_id, globals::mpi_comm_internode); + } + } + } +} +#endif + +void calculate_binned_opacities(const int modelgridindex) { + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + const auto rho = grid::get_rho(modelgridindex); + + const auto sys_time_start_calc = std::time(nullptr); + const auto temperature = grid::get_TR(modelgridindex); + + printout("calculating binned expansion opacities for cell %d...", modelgridindex); + + const auto t_mid = globals::timesteps[globals::timestep].mid; + + // find the first line with nu below the upper limit of the first bin + const auto *matchline = + std::lower_bound(&globals::linelist[0], &globals::linelist[globals::nlines], get_expopac_bin_nu_upper(0), + [](const auto &line, const double nu_cmf) -> bool { return line.nu > nu_cmf; }); + int lineindex = std::distance(globals::linelist, matchline); + + double kappa_planck_cumulative = 0.; + + for (size_t binindex = 0; binindex < expopac_nbins; binindex++) { + double bin_linesum = 0.; + + const auto nu_upper = get_expopac_bin_nu_upper(binindex); + + const auto nu_lower = get_expopac_bin_nu_lower(binindex); + const auto nu_mid = (nu_upper + nu_lower) / 2.; + + const auto delta_nu = nu_upper - nu_lower; + + while (lineindex < globals::nlines && globals::linelist[lineindex].nu >= nu_lower) { + const float tau_line = get_tau_sobolev(modelgridindex, lineindex, t_mid, false); + const auto linelambda = 1e8 * CLIGHT / globals::linelist[lineindex].nu; + bin_linesum += (linelambda / expopac_deltalambda) * -std::expm1(-tau_line); + lineindex++; + } + + const float bin_kappa_bb = 1. / (CLIGHT * t_mid * rho) * bin_linesum; + assert_always(std::isfinite(bin_kappa_bb)); + expansionopacities[nonemptymgi * expopac_nbins + binindex] = bin_kappa_bb; + + if constexpr (EXPANSION_OPAC_SAMPLE_KAPPAPLANCK) { + // static thread_local struct Rpkt_continuum_absorptioncoeffs chi_rpkt_cont { + // .nu = NAN, .total = NAN, .ffescat = NAN, .ffheat = NAN, .bf = NAN, .modelgridindex = -1, .timestep = -1 + // }; + // calculate_chi_rpkt_cont(nu_mid, chi_rpkt_cont, nullptr, modelgridindex); + // const auto bin_kappa_cont = chi_rpkt_cont.total / rho; + const auto bin_kappa_cont = calculate_chi_ffheating(modelgridindex, nu_mid) / rho; + + const auto planck_val = radfield::dbb(nu_mid, temperature, 1); + const auto kappa_planck = (bin_kappa_bb + bin_kappa_cont) * planck_val; + + kappa_planck_cumulative += kappa_planck * delta_nu; + + expansionopacity_planck_cumulative[nonemptymgi * expopac_nbins + binindex] = kappa_planck_cumulative; + } + } + printout("took %ld seconds\n", std::time(nullptr) - sys_time_start_calc); +} \ No newline at end of file diff --git a/rpkt.h b/rpkt.h index e32dc23a9..497fa49be 100644 --- a/rpkt.h +++ b/rpkt.h @@ -1,20 +1,49 @@ +#pragma once #ifndef RPKT_H #define RPKT_H +#include +#include + +struct Rpkt_continuum_absorptioncoeffs { + double nu{-1.}; // frequency at which opacity was calculated + double total{0.}; + double ffescat{0.}; + double ffheat{0.}; + double bf{0.}; + double ffheating{0.}; + // double bfheating; + int modelgridindex{-1}; + int timestep{-1}; +}; + +struct Phixslist { + std::vector groundcont_gamma_contr; // for either USE_LUT_PHOTOION = true or !USE_LUT_BFHEATING = false + std::vector chi_bf_sum; + std::vector gamma_contr; // needed for DETAILED_BF_ESTIMATORS_ON + int allcontend{-1}; + int allcontbegin{0}; + int bfestimend{-1}; + int bfestimbegin{0}; +}; + #include "artisoptions.h" -#include "constants.h" +#include "atomic.h" #include "grid.h" #include "sn3d.h" -void do_rpkt(struct packet *pkt_ptr, double t2); -void emit_rpkt(struct packet *pkt_ptr); -auto closest_transition(double nu_cmf, int next_trans) -> int; -auto calculate_chi_bf_gammacontr(int modelgridindex, double nu) -> double; -void calculate_chi_rpkt_cont(double nu_cmf, struct rpkt_continuum_absorptioncoeffs *chi_rpkt_cont_thisthread, - int modelgridindex, bool usecellhistupdatephixslist); +void do_rpkt(Packet &pkt, double t2); +void emit_rpkt(Packet &pkt); +[[nodiscard]] auto closest_transition(double nu_cmf, int next_trans) -> int; +void calculate_chi_rpkt_cont(double nu_cmf, Rpkt_continuum_absorptioncoeffs &chi_rpkt_cont, Phixslist *phixslist, + int modelgridindex); +[[nodiscard]] auto sample_planck_times_expansion_opacity(int nonemptymgi) -> double; +void allocate_expansionopacities(); +void calculate_binned_opacities(int modelgridindex); +void MPI_Bcast_binned_opacities(int modelgridindex, int root_node_id); -constexpr auto get_linedistance(const double prop_time, const double nu_cmf, const double nu_trans, - const double d_nu_on_d_l) -> double { +[[nodiscard]] 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) { @@ -37,11 +66,9 @@ constexpr auto get_linedistance(const double prop_time, const double nu_cmf, con const int ion) -> int { assert_testmodeonly(ion >= 0); assert_testmodeonly(ion < get_nions(element) - 1); - return nonemptymgi * get_includedions() + get_uniqueionindex(element, ion); -} - -[[nodiscard]] inline auto get_ionestimindex(const int mgi, const int element, const int ion) -> int { - return get_ionestimindex_nonemptymgi(grid::get_modelcell_nonemptymgi(mgi), element, ion); + const int groundcontindex = globals::elements[element].ions[ion].groundcontindex; + assert_always(groundcontindex >= 0); + return nonemptymgi * globals::nbfcontinua_ground + groundcontindex; } #endif // RPKT_H diff --git a/scripts/artis-cosma8.sh b/scripts/artis-cosma8.sh old mode 100755 new mode 100644 index 2eb2391d8..6f395e3bc --- a/scripts/artis-cosma8.sh +++ b/scripts/artis-cosma8.sh @@ -41,7 +41,7 @@ mkdir ${SLURM_JOB_ID}.slurm if grep -q "RESTART_NEEDED" "output_0-0.txt" then - sbatch ./artis-cosma8.sh + sbatch ./artis/scripts/artis-cosma8.sh # sbatch $SLURM_JOB_NAME fi diff --git a/scripts/artis-gadi.sh b/scripts/artis-gadi.sh deleted file mode 100755 index ee265cf5f..000000000 --- a/scripts/artis-gadi.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -#PBS -P fm5 -#PBS -q normal -#PBS -l walltime=24:00:00 -#PBS -l mem=1920GB -#PBS -l ncpus=960 -#PBS -l wd -##PBS -m abe -##PBS -M luke.shingles@gmail.com - -# ncpus must be a factor of the cores per node -# mem is total memory (all cores combined) - -# gadi normal queue Cascade Lake -# 2x24 (48) cores per node -# 192GB per node (4GB per core) - -# gadi normal queue -# Charge rate: 2 SU per resource-hour (walltime) -# "one resource" is the higher of CPU request, or memory request divided by 4GB - -module load gsl -module load intel-compiler -module unload intel-mpi -module load openmpi - -mpirun ./sn3d -w 24 > out.txt - -mkdir ${PBS_JOBID} -./artis/scripts/movefiles.sh ${PBS_JOBID} - -if grep -q "RESTART_NEEDED" "output_0-0.txt" -then - qsub $PBS_JOBNAME -fi - -if [ -f packets00_0000.out ]; then - qsub ./artis/scripts/exspec-gzip-gadi_raijin.sh -fi \ No newline at end of file diff --git a/scripts/artis-raijin.sh b/scripts/artis-raijin.sh deleted file mode 100755 index 85c03b051..000000000 --- a/scripts/artis-raijin.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -#PBS -P fm5 -#PBS -q normal -#PBS -l walltime=10:00:00 -#PBS -l mem=1920GB -#PBS -l ncpus=960 -#PBS -l wd -##PBS -m abe -##PBS -M luke.shingles@gmail.com - -# ncpus must be a factor of the cores per node -# mem is total memory (all cores combined) - -# on raijin normal queue Sandy Bridge: -# 16 cores per node -# 66% of nodes 32GB = 2GB per core -# 31% of nodes 64GB = 4GB per core -# 2% of nodes 128GB = 8GB per core - -# raijin normalbw queue Broadwell: -# 28 cores per node -# 536 nodes 128GB = 4.57 GB per core -# 268 nodes 256GB = 9.14 GB per core -# 10 nodes 1TB = 36.57 GB per core -# -# raijin normal/normlbw walltime limits: -# 48 hours for 1-255 cores -# 24 hours for 256-511 cores -# 10 hours for 512-1023 cores -# 5 hours for >=1024 cores - -module load gsl -module load intel-mpi -module load intel-cc/2018.3.222 - -ulimit -l 2097152 - -mpirun ./sn3d -w 10 > out.txt - -mkdir ${PBS_JOBID} -./artis/scripts/movefiles.sh ${PBS_JOBID} - -if grep -q "RESTART_NEEDED" "output_0-0.txt" -then - qsub $PBS_JOBNAME -fi - -if [ -f packets00_0000.out ]; then - qsub ./artis/scripts/exspec-gzip-gadi_raijin.sh -fi \ No newline at end of file diff --git a/scripts/artis-virgo-slurmjob.sh b/scripts/artis-virgo-slurmjob.sh index ada41a90a..3542df568 100755 --- a/scripts/artis-virgo-slurmjob.sh +++ b/scripts/artis-virgo-slurmjob.sh @@ -4,7 +4,9 @@ cd $SLURM_SUBMIT_DIR -#spack load gsl%gcc target=$(spack arch -t) +eval `spack load --sh gsl%gcc arch=linux-debian11-x86_64` + +export LD_LIBRARY_PATH=$(gsl-config --prefix)/lib/:$LD_LIBRARY_PATH echo "CPU type: $(c++ -march=native -Q --help=target | grep -- '-march= ' | cut -f3)" diff --git a/scripts/artis-virgo-submit.sh b/scripts/artis-virgo-submit.sh index 8d924a973..e66f20186 100755 --- a/scripts/artis-virgo-submit.sh +++ b/scripts/artis-virgo-submit.sh @@ -1,6 +1,4 @@ #!/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=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 +export APPTAINER_SHARENS=true +export APPTAINER_CONFIGDIR=/tmp/$USER +sbatch -J $(basename $(exec pwd)) --ntasks=1920 --ntasks-per-node=128 --mem-per-cpu=2000MB --partition=long --time=24:00:00 --constraint=amd,epyc,7713 --mail-type=ALL --mail-user=${USER}@gsi.de --no-requeue -- artis/scripts/artis-virgo-slurmjob.sh diff --git a/scripts/clean.sh b/scripts/clean.sh index adc9b1e61..41d5e303b 100755 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -paths="*.tmp *.out *.out.* out.txt output_*-*.txt exspec.txt machine.file.* core.* *.slurm packets bflist.dat logfiles.tar*" +paths="*.tmp *.out *.out.* out.txt output_*-*.txt exspec.txt machine.file.* core.* *.slurm packets vspecpol speclc_angle_res bflist.dat logfiles.tar*" # if [[ "$1" == "-d" ]]; then # echo 1 @@ -13,7 +13,7 @@ if [ 0 -lt $(ls $paths 2>/dev/null | wc -w) ]; then echo "The following ARTIS run files will be deleted:" ls -d -- $paths 2>/dev/null - read -p "Are you sure you want to delete these ARTIS run files? " -n 1 -r + read -p "Are you sure you want to delete these ARTIS run files? [y/n]" -n 1 -r echo # (optional) move to a new line if [[ $REPLY =~ ^[Yy]$ ]]; then echo "Deleting:" diff --git a/scripts/exspec-after.sh b/scripts/exspec-after.sh index 5c336b625..874db2b6f 100755 --- a/scripts/exspec-after.sh +++ b/scripts/exspec-after.sh @@ -3,27 +3,32 @@ # only compress the files if we successfully ran exspec if [ -f emission.out* ]; then - if command -v zstd > /dev/null; then - # zstd does decent compression at high speeds - cmdcompress="zstd -T0 -16 -v --rm -f" - elif command -v lz4 > /dev/null; then - # lz4 is extremely fast, but low compression ratios - cmdcompress="lz4 -v --best --rm -f" - else - # fall back to gzip - cmdcompress="gzip -v -f" - fi + # zstd does decent compression at high speeds + cmdcompress="zstd -T0 -16 -v --rm -f" + + # fall back to gzip + # cmdcompress="gzip -v -f" # join 3D direction files, if they exist - ./artis/scripts/mergeangleres.py + python3 ./artis/scripts/mergeangleres.py mkdir -p packets mv packets*.out* packets/ || true + mkdir -p vpackets + mv vpackets*.out* vpackets/ || true + + mkdir -p vspecpol + mv vspecpol*.out* vspecpol/ || true + + # remove empty directories + find . -maxdepth 1 -type d -empty -delete + # 3D kilonova model.txt and abundances.txt can be huge, so compress txt files # do maxdepth 1 first in case job gets killed during run folder compression find . -maxdepth 1 -name '*.txt' ! -name "output_0-0.txt" -size +2M -exec $cmdcompress {} \; find . -maxdepth 1 -name '*.out' ! -name "slurm-*.out" -size +1M -exec $cmdcompress {} \; + find . -maxdepth 1 -name 'rateceoff.dat' ! -name "slurm-*.out" -size +1M -exec $cmdcompress {} \; find packets/ -name 'packets*.out' -size +1M -exec $cmdcompress {} \; @@ -32,5 +37,17 @@ if [ -f emission.out* ]; then ./artis/scripts/tar_rm_logs.sh + mkdir -p speclc_angle_res + mv *_res_*.out* speclc_angle_res/ || true + + # convert packets to parquet for fast reading + artistools lc --frompackets || true + + # convert virtual packets to parquet + artistools lc --frompackets -plotvspecpol 0 || true + + # convert estimators to parquet. commented because python multiprocessing hangs on JUWELS + #python3 -c 'import artistools as at; at.estimators.scan_estimators()' || true + fi diff --git a/scripts/exspec-before.sh b/scripts/exspec-before.sh index 006481497..fcf266cbf 100755 --- a/scripts/exspec-before.sh +++ b/scripts/exspec-before.sh @@ -5,19 +5,16 @@ if [[ ! -f packets00_0000.out* && -f packets/packets00_0000.out* ]]; then mv packets/packets*.out* . fi -find . -maxdepth 1 -name 'packets**.out.zst' -exec zst -d -v -T0 --rm {} \; -find . -maxdepth 1 -name 'packets**.out.lz4' -exec lz4 -d -v --rm {} \; +find . -maxdepth 1 -name 'packets**.out.zst' -exec zstd -d -v -T0 --rm {} \; find . -maxdepth 1 -name 'packets**.out.gz' -exec gzip -d -v {} \; find . -maxdepth 1 -name 'packets**.out.xz' -exec xz -d -v -T0 {} \; #unzip, e.g. phixsdata_v2.txt.xz transitiondata.txt.xz ratecoeff.dat.xz -find . -maxdepth 1 -name '*.dat.zst' -exec zst -d -v -T0 --rm {} \; -find . -maxdepth 1 -name '*.dat.lz4' -exec lz4 -d -v --rm {} \; +find . -maxdepth 1 -name '*.dat.zst' -exec zstd -d -v -T0 --rm {} \; find . -maxdepth 1 -name '*.dat.xz' -exec xz -d -v -T0 {} \; find . -maxdepth 1 -name '*.dat.gz' -exec gzip -d -v {} \; -find . -maxdepth 1 -name '*.txt.zst' -exec zst -d -v -T0 --rm {} \; -find . -maxdepth 1 -name '*.txt.lz4' -exec lz4 -d -v --rm {} \; +find . -maxdepth 1 -name '*.txt.zst' -exec zstd -d -v -T0 --rm {} \; find . -maxdepth 1 -name '*.txt.xz' -exec xz -d -v -T0 {} \; find . -maxdepth 1 -name '*.txt.gz' -exec gzip -d -v {} \; diff --git a/scripts/exspec-gzip-gadi_raijin.sh b/scripts/exspec-gzip-gadi_raijin.sh deleted file mode 100755 index 4fdd4f270..000000000 --- a/scripts/exspec-gzip-gadi_raijin.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -#PBS -P fm5 -#PBS -q express -#PBS -l walltime=12:00:00 -#PBS -l mem=4GB -#PBS -l ncpus=1 -#PBS -l wd -#PBS -m abe - -# ncpus must be a factor of the cores per node (16) -# mem is total memory (all cores combined) -# on raijin, max 128GB/node = 8GB per core and MPI task (2% of nodes) -# max 64GB/node = 4GB per core (31% of nodes) -# max 32GB/node = 2GB per core (66% of nodes) -# normal queue walltime limits -# 48 hrs for 1-255 cores -# 24 hrs for 256-511 cores -# 10 hrs for 512-1023 cores -# 5 hours for 1024-56960 cores - -ulimit -l 2097152 - -module load gsl/2.5 -module load intel-mpi/2018.3.222 -module load intel-cc/2018.3.222 - -if [ ! -f emission.out.zst ]; then - ./artis/scripts/exspec-before.sh - ./exspec -else - echo 'Not running exspec because emission.out.zst was found' -fi - -./artis/scripts/exspec-after.sh diff --git a/scripts/exspec-gzip-virgo-slurmjob.sh b/scripts/exspec-gzip-virgo-slurmjob.sh index e9ac5c4a8..836b380c9 100755 --- a/scripts/exspec-gzip-virgo-slurmjob.sh +++ b/scripts/exspec-gzip-virgo-slurmjob.sh @@ -1,6 +1,6 @@ #!/bin/bash -x -spack load gsl target=$(spack arch -t) +eval `spack load --sh gsl%gcc arch=linux-debian11-x86_64` cd $SLURM_SUBMIT_DIR diff --git a/scripts/mergeangleres.py b/scripts/mergeangleres.py index 3052b4490..eb93571f5 100755 --- a/scripts/mergeangleres.py +++ b/scripts/mergeangleres.py @@ -4,7 +4,7 @@ def resfilepath(fileprefix: str, abin: int) -> Path: - return Path(fileprefix + f"_res_{abin:02d}.out") + return Path(f"{fileprefix}_res_{abin:02d}.out") def main() -> None: @@ -12,14 +12,14 @@ def main() -> None: if not resfilepath(fileprefix, 0).is_file(): continue - outfile = Path(fileprefix + "_res.out") + outfile = Path(f"{fileprefix}_res.out") outfile.unlink(missing_ok=True) - Path(fileprefix + "_res.out.xz").unlink(missing_ok=True) + Path(f"{fileprefix}_res.out.xz").unlink(missing_ok=True) print(f"Merging {fileprefix}_res_??.out into {outfile}") with outfile.open("wt", encoding="utf-8") as fout: - for abin in range(0, 100): + for abin in range(100): fout.writelines(resfilepath(fileprefix, abin).open("rt", encoding="utf-8").readlines()) diff --git a/scripts/tar_rm_logs.sh b/scripts/tar_rm_logs.sh index 0170862c2..a2aef2c2b 100755 --- a/scripts/tar_rm_logs.sh +++ b/scripts/tar_rm_logs.sh @@ -17,6 +17,7 @@ if [ -f logfiles.tar* ]; then echo "logfiles.tar* already exists! Not overwriting" exit 1 else - tar -cvf $tmpdir/logfiles.tar */output_*.txt* && zstd -v -T0 -15 $tmpdir/logfiles.tar && mv -v $tmpdir/logfiles.tar.zst . && rm -rfv $tmpdir && find . -mindepth 2 -name "output_*.txt*" ! -name "output_0-0.txt*" -delete + find . -mindepth 2 -name "output_*.txt*" -print > $tmpdir/filelist.txt + tar -cvf $tmpdir/logfiles.tar --files-from $tmpdir/filelist.txt && zstd -v -T0 -15 $tmpdir/logfiles.tar && mv -v $tmpdir/logfiles.tar.zst . && rm -rfv $tmpdir && find . -mindepth 2 -name "output_*.txt*" ! -name "output_0-0.txt*" -delete fi diff --git a/sn3d.cc b/sn3d.cc index 40c249a8f..b9d2693bc 100644 --- a/sn3d.cc +++ b/sn3d.cc @@ -12,42 +12,68 @@ #include "sn3d.h" +#ifdef MPI_ON +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include #include +#include +#include +#include +#ifdef STDPAR_ON +#include +#endif #include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "decay.h" #include "gammapkt.h" #include "globals.h" #include "grid.h" #include "input.h" +#include "macroatom.h" +#ifdef MPI_ON +#include "rpkt.h" +#endif #include "nltepop.h" #include "nonthermal.h" +#include "packet.h" #include "radfield.h" #include "ratecoeff.h" -#include "rpkt.h" -#include "spectrum.h" +#include "spectrum_lightcurve.h" #include "stats.h" #include "update_grid.h" #include "update_packets.h" #include "version.h" #include "vpkt.h" -// threadprivate variables -int tid; -bool use_cellhist; -std::mt19937 stdrng(std::random_device{}()); -gsl_integration_workspace *gslworkspace = nullptr; -FILE *output_file = nullptr; -static FILE *linestat_file = nullptr; -static time_t real_time_start = -1; -static time_t time_timestep_start = -1; // this will be set after the first update of the grid and before packet prop -static FILE *estimators_file = nullptr; +std::mt19937 stdrng{std::random_device{}()}; + +std::ofstream output_file; + +namespace { + +FILE *linestat_file{}; +auto real_time_start = -1; +auto time_timestep_start = -1; // this will be set after the first update of the grid and before packet prop +FILE *estimators_file{}; +#ifdef MPI_ON size_t mpi_grid_buffer_size = 0; -char *mpi_grid_buffer = nullptr; +char *mpi_grid_buffer{}; +#endif -static void initialise_linestat_file() { +void initialise_linestat_file() { if (globals::simulation_continued_from_saved && !RECORD_LINESTAT) { // only write linestat.out on the first run, unless it contains statistics for each timestep return; @@ -81,12 +107,11 @@ static void initialise_linestat_file() { fprintf(linestat_file, "\n"); fflush(linestat_file); - // setvbuf(linestat_file,nullptr, _IOLBF, 1); // flush after every line makes it slow! } -static void write_deposition_file(const int nts, const int my_rank, const int nstart, const int ndo) { +void write_deposition_file(const int nts, const int my_rank, const int nstart, const int ndo) { printout("Calculating deposition rates...\n"); - time_t const time_write_deposition_file_start = time(nullptr); + auto const time_write_deposition_file_start = std::time(nullptr); double mtot = 0.; // calculate analytical decay rates @@ -186,12 +211,12 @@ static void write_deposition_file(const int nts, const int my_rank, const int ns } printout("calculating and writing deposition.out took %ld seconds\n", - time(nullptr) - time_write_deposition_file_start); + std::time(nullptr) - time_write_deposition_file_start); } #ifdef MPI_ON -static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, const int nstart, const int ndo, - char *mpi_grid_buffer, const size_t mpi_grid_buffer_size) { +void mpi_communicate_grid_properties(const int my_rank, const int nprocs, const int nstart, const int ndo, + char *mpi_grid_buffer, const size_t mpi_grid_buffer_size) { int position = 0; for (int root = 0; root < nprocs; root++) { MPI_Barrier(MPI_COMM_WORLD); @@ -210,6 +235,7 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, radfield::do_MPI_Bcast(modelgridindex, root, root_node_id); nonthermal::nt_MPI_Bcast(modelgridindex, root); + 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); @@ -219,13 +245,13 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, const auto nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); assert_always(globals::corrphotoionrenorm != nullptr); if (globals::rank_in_node == 0) { - MPI_Bcast(&globals::corrphotoionrenorm[nonemptymgi * get_includedions()], get_includedions(), MPI_DOUBLE, - root_node_id, globals::mpi_comm_internode); + MPI_Bcast(&globals::corrphotoionrenorm[nonemptymgi * globals::nbfcontinua_ground], + globals::nbfcontinua_ground, MPI_DOUBLE, root_node_id, globals::mpi_comm_internode); } assert_always(globals::gammaestimator != nullptr); - MPI_Bcast(&globals::gammaestimator[nonemptymgi * get_includedions()], get_includedions(), MPI_DOUBLE, root, - MPI_COMM_WORLD); + MPI_Bcast(&globals::gammaestimator[nonemptymgi * globals::nbfcontinua_ground], globals::nbfcontinua_ground, + MPI_DOUBLE, root, MPI_COMM_WORLD); } assert_always(grid::modelgrid[modelgridindex].elem_meanweight != nullptr); @@ -233,6 +259,8 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, MPI_Bcast(grid::modelgrid[modelgridindex].elem_meanweight, get_nelements(), MPI_FLOAT, root_node_id, globals::mpi_comm_internode); } + + MPI_Bcast_binned_opacities(modelgridindex, root_node_id); } if (root == my_rank) { @@ -275,7 +303,7 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, } } } - printout("[info] mem_usage: MPI_BUFFER: used %d of %d bytes allocated to mpi_grid_buffer\n", position, + printout("[info] mem_usage: MPI_BUFFER: used %d of %zu bytes allocated to mpi_grid_buffer\n", position, mpi_grid_buffer_size); assert_always(static_cast(position) <= mpi_grid_buffer_size); } @@ -331,26 +359,26 @@ static void mpi_communicate_grid_properties(const int my_rank, const int nprocs, MPI_Barrier(MPI_COMM_WORLD); } -static void mpi_reduce_estimators(int nts) { +void mpi_reduce_estimators(int nts) { + const int nonempty_npts_model = grid::get_nonempty_npts_model(); radfield::reduce_estimators(); MPI_Barrier(MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, globals::ffheatingestimator, grid::get_npts_model(), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, globals::colheatingestimator, grid::get_npts_model(), MPI_DOUBLE, MPI_SUM, - MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, globals::ffheatingestimator, nonempty_npts_model, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, globals::colheatingestimator, nonempty_npts_model, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); - const int arraylen = grid::get_nonempty_npts_model() * get_includedions(); - if constexpr (USE_LUT_PHOTOION) { MPI_Barrier(MPI_COMM_WORLD); assert_always(globals::gammaestimator != nullptr); - MPI_Allreduce(MPI_IN_PLACE, globals::gammaestimator, arraylen, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, globals::gammaestimator, nonempty_npts_model * globals::nbfcontinua_ground, MPI_DOUBLE, + MPI_SUM, MPI_COMM_WORLD); } if constexpr (USE_LUT_BFHEATING) { MPI_Barrier(MPI_COMM_WORLD); assert_always(globals::bfheatingestimator != nullptr); - MPI_Allreduce(MPI_IN_PLACE, globals::bfheatingestimator, arraylen, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, globals::bfheatingestimator, nonempty_npts_model * globals::nbfcontinua_ground, + MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); } if constexpr (RECORD_LINESTAT) { @@ -361,8 +389,9 @@ static void mpi_reduce_estimators(int nts) { MPI_Allreduce(MPI_IN_PLACE, globals::acounter, globals::nlines, MPI_INT, MPI_SUM, MPI_COMM_WORLD); } - assert_always(globals::rpkt_emiss != nullptr); - MPI_Allreduce(MPI_IN_PLACE, globals::rpkt_emiss, grid::get_npts_model(), MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + assert_always(static_cast(globals::dep_estimator_gamma.size()) == nonempty_npts_model); + MPI_Allreduce(MPI_IN_PLACE, globals::dep_estimator_gamma.data(), nonempty_npts_model, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); @@ -393,7 +422,7 @@ static void mpi_reduce_estimators(int nts) { } #endif -static void write_temp_packetsfile(const int timestep, const int my_rank, const struct packet *const pkt) { +void write_temp_packetsfile(const int timestep, const int my_rank, const Packet *pkt) { // write packets binary file (and retry if the write fails) char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "packets_%.4d_ts%d.tmp", my_rank, timestep); @@ -406,8 +435,8 @@ static void write_temp_packetsfile(const int timestep, const int my_rank, const printout("ERROR: Could not open file '%s' for mode 'wb'. \n", filename); write_success = false; } else { - write_success = (std::fwrite(pkt, sizeof(struct packet), globals::npkts, packets_file) == - static_cast(globals::npkts)); + write_success = + (std::fwrite(pkt, sizeof(Packet), globals::npkts, packets_file) == static_cast(globals::npkts)); if (!write_success) { printout("fwrite() FAILED! will retry...\n"); } @@ -421,39 +450,39 @@ static void write_temp_packetsfile(const int timestep, const int my_rank, const } } -static void remove_temp_packetsfile(const int timestep, const int my_rank) { +void remove_temp_packetsfile(const int timestep, const int my_rank) { char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "packets_%.4d_ts%d.tmp", my_rank, timestep); if (access(filename, F_OK) == 0) { - remove(filename); + std::remove(filename); printout("Deleted %s\n", filename); } } -static void remove_grid_restart_data(const int timestep) { +void remove_grid_restart_data(const int timestep) { char prevfilename[MAXFILENAMELENGTH]; snprintf(prevfilename, MAXFILENAMELENGTH, "gridsave_ts%d.tmp", timestep); if (access(prevfilename, F_OK) == 0) { - remove(prevfilename); + std::remove(prevfilename); printout("Deleted %s\n", prevfilename); } } -static auto walltime_sufficient_to_continue(const int nts, const int nts_prev, const int walltimelimitseconds) -> bool { +auto walltime_sufficient_to_continue(const int nts, const int nts_prev, const int walltimelimitseconds) -> bool { #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif // time is measured from just before packet propagation from one timestep to the next - const int estimated_time_per_timestep = time(nullptr) - time_timestep_start; + const int estimated_time_per_timestep = std::time(nullptr) - time_timestep_start; printout("TIME: time between timesteps is %d seconds (measured packet prop of ts %d and update grid of ts %d)\n", estimated_time_per_timestep, nts_prev, nts); bool do_this_full_loop = true; if (walltimelimitseconds > 0) { - const int wallclock_used_seconds = time(nullptr) - real_time_start; + const int wallclock_used_seconds = std::time(nullptr) - real_time_start; const int wallclock_remaining_seconds = walltimelimitseconds - wallclock_used_seconds; printout("TIMED_RESTARTS: Used %d of %d seconds of wall time.\n", wallclock_used_seconds, walltimelimitseconds); @@ -475,36 +504,36 @@ static auto walltime_sufficient_to_continue(const int nts, const int nts_prev, c return do_this_full_loop; } -static void save_grid_and_packets(const int nts, const int my_rank, struct packet *packets) { +void save_grid_and_packets(const int nts, const int my_rank, Packet *packets) { #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif bool write_verified_sucess = false; while (!write_verified_sucess) { - const time_t time_write_packets_file_start = time(nullptr); + const auto time_write_packets_file_start = std::time(nullptr); printout("time before write temporary packets file %ld\n", time_write_packets_file_start); // save packet state at start of current timestep (before propagation) write_temp_packetsfile(nts, my_rank, packets); - vpkt_write_timestep(nts, my_rank, tid, false); + vpkt_write_timestep(nts, my_rank, false); - const time_t time_write_packets_file_finished = time(nullptr); + const auto time_write_packets_file_finished = std::time(nullptr); #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif printout("time after write temporary packets file %ld (took %ld seconds, waited %ld s for other ranks)\n", - time(nullptr), time_write_packets_file_finished - time_write_packets_file_start, - time(nullptr) - time_write_packets_file_finished); + std::time(nullptr), time_write_packets_file_finished - time_write_packets_file_start, + std::time(nullptr) - time_write_packets_file_finished); #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif - const time_t time_readback_packets_start = time(nullptr); + const auto time_readback_packets_start = std::time(nullptr); printout("reading back temporary packets file to check validity...\n"); @@ -515,7 +544,8 @@ static void save_grid_and_packets(const int nts, const int my_rank, struct packe MPI_Barrier(MPI_COMM_WORLD); #endif - printout("Verifying packets files for all ranks took %ld seconds.\n", time(nullptr) - time_readback_packets_start); + printout("Verifying packets files for all ranks took %ld seconds.\n", + std::time(nullptr) - time_readback_packets_start); } if (my_rank == 0) { @@ -539,41 +569,37 @@ static void save_grid_and_packets(const int nts, const int my_rank, struct packe } } -static void zero_estimators() { +void zero_estimators() { #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif - for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { - const auto modelgridindex = grid::get_mgi_of_nonemptymgi(nonemptymgi); - radfield::zero_estimators(modelgridindex); - - globals::ffheatingestimator[modelgridindex] = 0.; - globals::colheatingestimator[modelgridindex] = 0.; - - if constexpr (TRACK_ION_STATS) { + radfield::zero_estimators(); + if constexpr (TRACK_ION_STATS) { + for (int nonemptymgi = 0; nonemptymgi < grid::get_nonempty_npts_model(); nonemptymgi++) { + const auto modelgridindex = grid::get_mgi_of_nonemptymgi(nonemptymgi); stats::reset_ion_stats(modelgridindex); } + } - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < (get_nions(element) - 1); ion++) { - if constexpr (USE_LUT_PHOTOION) { - globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)] = 0.; - } - if constexpr (USE_LUT_BFHEATING) { - globals::bfheatingestimator[get_ionestimindex(modelgridindex, element, ion)] = 0.; - } - } - } + std::fill_n(globals::ffheatingestimator, grid::get_nonempty_npts_model(), 0.); + std::fill_n(globals::colheatingestimator, grid::get_nonempty_npts_model(), 0.); + std::ranges::fill(globals::dep_estimator_gamma, 0.); - globals::rpkt_emiss[modelgridindex] = 0.; + if constexpr (USE_LUT_PHOTOION) { + std::fill_n(globals::gammaestimator, grid::get_nonempty_npts_model() * globals::nbfcontinua_ground, 0.); } + + if constexpr (USE_LUT_BFHEATING) { + std::fill_n(globals::bfheatingestimator, grid::get_nonempty_npts_model() * globals::nbfcontinua_ground, 0.); + } + #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); #endif } -static auto do_timestep(const int nts, const int titer, const int my_rank, const int nstart, const int ndo, - struct packet *packets, const int walltimelimitseconds) -> bool { +auto do_timestep(const int nts, const int titer, const int my_rank, const int nstart, const int ndo, Packet *packets, + const int walltimelimitseconds) -> bool { bool do_this_full_loop = true; const int nts_prev = (titer != 0 || nts == 0) ? nts : nts - 1; @@ -601,7 +627,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const update_grid(estimators_file, nts, nts_prev, my_rank, nstart, ndo, titer, real_time_start); - const time_t sys_time_start_communicate_grid = time(nullptr); + const auto sys_time_start_communicate_grid = std::time(nullptr); /// Each process has now updated its own set of cells. The results now need to be communicated between processes. #ifdef MPI_ON @@ -609,7 +635,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const #endif printout("timestep %d: time after grid properties have been communicated %ld (took %ld seconds)\n", nts, - time(nullptr), time(nullptr) - sys_time_start_communicate_grid); + std::time(nullptr), std::time(nullptr) - sys_time_start_communicate_grid); /// If this is not the 0th time step of the current job step, /// write out a snapshot of the grid properties for further restarts @@ -618,7 +644,7 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const save_grid_and_packets(nts, my_rank, packets); do_this_full_loop = walltime_sufficient_to_continue(nts, nts_prev, walltimelimitseconds); } - time_timestep_start = time(nullptr); + time_timestep_start = std::time(nullptr); // set all the estimators to zero before moving packets. This is now done // after update_grid so that, if requires, the gamma-ray heating estimator is known there @@ -631,33 +657,33 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const if ((nts < globals::timestep_finish) && do_this_full_loop) { /// Now process the packets. - update_packets(my_rank, nts, packets); + update_packets(my_rank, nts, std::span{packets, static_cast(globals::npkts)}); #ifdef MPI_ON // All the processes have their own versions of the estimators for this time step now. // Since these are going to be needed in the next time step, we will gather all the // estimators together now, sum them, and distribute the results - const time_t time_communicate_estimators_start = time(nullptr); + const auto time_communicate_estimators_start = std::time(nullptr); mpi_reduce_estimators(nts); #endif // The estimators have been summed across all proceses and distributed. // They will now be normalised independently on all processes - gammapkt::normalise_grey(nts); + gammapkt::normalise(nts); write_deposition_file(nts, my_rank, nstart, ndo); write_partial_lightcurve_spectra(my_rank, nts, packets); #ifdef MPI_ON - printout("timestep %d: time after estimators have been communicated %ld (took %ld seconds)\n", nts, time(nullptr), - time(nullptr) - time_communicate_estimators_start); + printout("timestep %d: time after estimators have been communicated %ld (took %ld seconds)\n", nts, + std::time(nullptr), std::time(nullptr) - time_communicate_estimators_start); #endif printout("During timestep %d on MPI process %d, %d pellets decayed and %d packets escaped. (t=%gd)\n", nts, my_rank, - globals::timesteps[nts].pellet_decays.load(), globals::nesc.load(), globals::timesteps[nts].mid / DAY); + globals::timesteps[nts].pellet_decays, globals::nesc, globals::timesteps[nts].mid / DAY); if (VPKT_ON) { printout("During timestep %d on MPI process %d, %d virtual packets were generated and %d escaped. \n", nts, @@ -696,23 +722,25 @@ static auto do_timestep(const int nts, const int titer, const int my_rank, const // snprintf(filename, MAXFILENAMELENGTH, "packets%.2d_%.4d.out", middle_iteration, my_rank); write_packets(filename, packets); - vpkt_write_timestep(nts, my_rank, tid, true); + vpkt_write_timestep(nts, my_rank, true); - printout("time after write final packets file %ld\n", time(nullptr)); + printout("time after write final packets file %ld\n", std::time(nullptr)); // final packets*.out have been written, so remove the temporary packets files // commented out because you might still want to resume the simulation // snprintf(filename, MAXFILENAMELENGTH, "packets%d_%d_odd.tmp", 0, my_rank); - // remove(filename); + // std::remove(filename); // snprintf(filename, MAXFILENAMELENGTH, "packets%d_%d_even.tmp", 0, my_rank); - // remove(filename); + // std::remove(filename); } } return !do_this_full_loop; } +} // anonymous namespace + auto main(int argc, char *argv[]) -> int { - real_time_start = time(nullptr); + real_time_start = std::time(nullptr); char filename[MAXFILENAMELENGTH]; // if DETAILED_BF_ESTIMATORS_ON is true, USE_LUT_PHOTOION must be false @@ -731,44 +759,40 @@ auto main(int argc, char *argv[]) -> int { globals::setup_mpi_vars(); - globals::startofline = std::make_unique(get_max_threads()); - if (globals::rank_global == 0) { - check_already_running(); - } - -// make sure rank 0 checked for a pid file before we proceed -#ifdef MPI_ON - MPI_Barrier(MPI_COMM_WORLD); -#endif + check_already_running(); const int my_rank = globals::rank_global; -#ifdef _OPENMP - /// Explicitly turn off dynamic threads because we use the threadprivate directive!!! +#if defined(_OPENMP) && !defined(GPU_ON) + // Explicitly turn off dynamic threads because we use the threadprivate directive!!! omp_set_dynamic(0); - #pragma omp parallel private(filename) #endif { - /// Get the current threads ID, copy it to a threadprivate variable - tid = get_thread_num(); - /// and initialise the threads outputfile - snprintf(filename, MAXFILENAMELENGTH, "output_%d-%d.txt", my_rank, tid); - output_file = fopen_required(filename, "w"); - /// Makes sure that the output_file is written line-by-line - setvbuf(output_file, nullptr, _IOLBF, 1); - globals::startofline[tid] = true; + /// initialise the thread and rank specific output file + snprintf(filename, MAXFILENAMELENGTH, "output_%d-%d.txt", my_rank, get_thread_num()); + output_file = std::ofstream(filename); + assert_always(output_file.is_open()); #ifdef _OPENMP - printout("OpenMP parallelisation is active with %d threads (max %d)\n", get_num_threads(), get_max_threads()); + printout("OpenMP parallelisation is active with %d threads (max %d)\n", omp_get_num_threads(), get_max_threads()); #else printout("OpenMP parallelisation is not enabled in this build (this is normal)\n"); #endif - - gslworkspace = gsl_integration_workspace_alloc(GSLWSIZE); } - printout("time at start %ld\n", real_time_start); +#ifdef STDPAR_ON + printout("C++ standard parallelism (stdpar) is enabled with %d hardware threads\n", + std::thread::hardware_concurrency()); +#endif + +#ifdef GPU_ON + printout("GPU_ON is enabled\n"); +#endif + + printout("time at start %d\n", real_time_start); + + printout("integration method is %s\n", USE_SIMPSON_INTEGRATOR ? "Simpson rule" : "GSL qag"); #ifdef WALLTIMELIMITSECONDS int walltimelimitseconds = WALLTIMELIMITSECONDS; @@ -785,11 +809,11 @@ auto main(int argc, char *argv[]) -> int { printout("walltimelimitseconds = %d\n", walltimelimitseconds); } else { fprintf(stderr, "Usage: %s [-w WALLTIMELIMITHOURS]\n", argv[0]); - abort(); + std::abort(); } } - auto *const packets = static_cast(calloc(MPKTS, sizeof(struct packet))); + auto *const packets = static_cast(malloc(MPKTS * sizeof(Packet))); assert_always(packets != nullptr); @@ -799,7 +823,6 @@ auto main(int argc, char *argv[]) -> int { printout("git status %s\n", GIT_STATUS); - // printout("Hash of most recent commit: %s\n",GIT_HASH); printout("sn3d compiled at %s on %s\n", __TIME__, __DATE__); #if defined TESTMODE && TESTMODE @@ -810,17 +833,12 @@ auto main(int argc, char *argv[]) -> int { printout("process id (pid): %d\n", getpid()); printout("MPI enabled:\n"); printout(" rank %d of [0..%d] in MPI_COMM_WORLD\n", globals::rank_global, globals::nprocs - 1); - printout(" node %d of [0..%d]\n", globals::node_id, globals::node_count - 1); - printout(" rank %d of [0..%d] within this node (MPI_COMM_WORLD_SHARED)\n", globals::rank_in_node, - globals::node_nprocs - 1); + printout(" rank %d of [0..%d] in MPI_COMM_WORLD_SHARED on node %d of [0..%d]\n", globals::rank_in_node, + globals::node_nprocs - 1, globals::node_id, globals::node_count - 1); #else printout("MPI is disabled in this build\n"); #endif - 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); @@ -832,7 +850,7 @@ auto main(int argc, char *argv[]) -> int { initialise_linestat_file(); } - printout("time after input %ld\n", time(nullptr)); + printout("time after input %ld\n", std::time(nullptr)); printout("timesteps %d\n", globals::ntimesteps); /// Precalculate the rate coefficients for spontaneous and stimulated recombination @@ -840,14 +858,12 @@ auto main(int argc, char *argv[]) -> int { /// T_R and W. W is easily factored out. For stimulated recombination we must assume /// T_e = T_R for this precalculation. - printout("time before tabulation of rate coefficients %ld\n", time(nullptr)); ratecoefficients_init(); - printout("time after tabulation of rate coefficients %ld\n", time(nullptr)); - // abort(); + #ifdef MPI_ON - printout("barrier after tabulation of rate coefficients: time before barrier %ld, ", time(nullptr)); + printout("barrier after tabulation of rate coefficients: time before barrier %ld, ", std::time(nullptr)); MPI_Barrier(MPI_COMM_WORLD); - printout("time after barrier %ld\n", time(nullptr)); + printout("time after barrier %ld\n", std::time(nullptr)); #endif stats::init(); @@ -856,7 +872,7 @@ auto main(int argc, char *argv[]) -> int { FILE *syn_file = fopen_required("syn_dir.txt", "w"); fprintf(syn_file, "%g %g %g", globals::syn_dir[0], globals::syn_dir[1], globals::syn_dir[2]); fclose(syn_file); - printout("time write syn_dir.txt file %ld\n", time(nullptr)); + printout("time write syn_dir.txt file %ld\n", std::time(nullptr)); bool terminate_early = false; @@ -867,13 +883,13 @@ auto main(int argc, char *argv[]) -> int { } /// Initialise the grid. Set up the initial positions and sizes of the grid cells. - printout("time grid_init %ld\n", time(nullptr)); + printout("time grid_init %ld\n", std::time(nullptr)); grid::grid_init(my_rank); printout("Simulation propagates %g packets per process (total %g with nprocs %d)\n", 1. * globals::npkts, 1. * globals::npkts * globals::nprocs, globals::nprocs); - printout("[info] mem_usage: packets occupy %.3f MB\n", MPKTS * sizeof(struct packet) / 1024. / 1024.); + printout("[info] mem_usage: packets occupy %.3f MB\n", MPKTS * sizeof(Packet) / 1024. / 1024.); if (!globals::simulation_continued_from_saved) { std::remove("deposition.out"); @@ -906,7 +922,7 @@ auto main(int argc, char *argv[]) -> int { /// 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 mpi_grid_buffer_size = 4 * ((12 + 4 * get_includedions()) * (maxndo) + 1); - printout("reserve mpi_grid_buffer_size %d space for MPI communication buffer\n", mpi_grid_buffer_size); + printout("reserve mpi_grid_buffer_size %zu space for MPI communication buffer\n", mpi_grid_buffer_size); mpi_grid_buffer = static_cast(malloc(mpi_grid_buffer_size * sizeof(char))); assert_always(mpi_grid_buffer != nullptr); MPI_Barrier(MPI_COMM_WORLD); @@ -926,14 +942,14 @@ auto main(int argc, char *argv[]) -> int { } // initialise or read in virtual packet spectra - vpkt_init(nts, my_rank, tid, globals::simulation_continued_from_saved); + vpkt_init(nts, my_rank, globals::simulation_continued_from_saved); while (nts < globals::timestep_finish && !terminate_early) { globals::timestep = nts; #ifdef MPI_ON - // const time_t time_before_barrier = time(nullptr); + // const auto time_before_barrier = std::time(nullptr); MPI_Barrier(MPI_COMM_WORLD); - // const time_t time_after_barrier = time(nullptr); + // const auto time_after_barrier = std::time(nullptr); // printout("timestep %d: time before barrier %d, time after barrier %d\n", nts, time_before_barrier, // time_after_barrier); #endif @@ -980,10 +996,10 @@ auto main(int argc, char *argv[]) -> int { MPI_Barrier(MPI_COMM_WORLD); #endif - const time_t real_time_end = time(nullptr); - 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); + const auto real_time_end = std::time(nullptr); + printout("sn3d finished at %ld (this job wallclock hours %.2f * %d processes * %d threads = %.1f core hours)\n", + real_time_end, (real_time_end - real_time_start) / 3600., globals::nprocs, get_max_threads(), + (real_time_end - real_time_start) / 3600. * globals::nprocs * get_max_threads()); if (estimators_file != nullptr) { fclose(estimators_file); @@ -995,21 +1011,7 @@ auto main(int argc, char *argv[]) -> int { radfield::close_file(); nonthermal::close_file(); -#ifdef _OPENMP -#pragma omp parallel -#endif - { fclose(output_file); } - -#ifdef _OPENMP - omp_set_dynamic(0); -#pragma omp parallel -#endif - { gsl_integration_workspace_free(gslworkspace); } - free(packets); - if constexpr (TRACK_ION_STATS) { - stats::cleanup(); - } decay::cleanup(); diff --git a/sn3d.h b/sn3d.h index 610bf1547..5c0b6dfdb 100644 --- a/sn3d.h +++ b/sn3d.h @@ -1,3 +1,4 @@ +#pragma once #ifndef SN3D_H #define SN3D_H @@ -6,6 +7,30 @@ #include #include +#ifdef STDPAR_ON +#include + +#ifndef __cpp_lib_execution +// homebrew llvm doesn't support execution policy yet, so brew install onedpl tbb +#include +#include +#endif + +#define EXEC_PAR_UNSEQ std::execution::par_unseq, +#define EXEC_PAR std::execution::par, +#else +#define EXEC_PAR_UNSEQ +#define EXEC_PAR +#endif + +#ifdef _OPENMP +#include +#endif + +#ifdef MPI_ON +#include +#endif + #include #include #include @@ -14,39 +39,72 @@ #include #include #include +#include -// #define _OPENMP -#ifdef _OPENMP -#include -#endif +#include "constants.h" -#ifdef MPI_ON -#include -#endif - -extern FILE *output_file; -extern int tid; -extern bool use_cellhist; +constexpr int cellcacheslotid = 0; +inline bool use_cellcache = false; extern std::mt19937 stdrng; -extern gsl_integration_workspace *gslworkspace; +extern std::ofstream output_file; + +inline char outputlinebuf[1024] = ""; +inline bool outputstartofline = true; +inline struct tm timebuf {}; + +// if not set, force Simpson integrator on GPU mode (since gsl doesn't work there!) +#ifndef USE_SIMPSON_INTEGRATOR +#define USE_SIMPSON_INTEGRATOR false +#endif + +inline void nop(gsl_integration_workspace *w) {}; + +inline thread_local auto gslworkspace = + std::unique_ptr{ + USE_SIMPSON_INTEGRATOR ? nullptr : gsl_integration_workspace_alloc(GSLWSIZE), + USE_SIMPSON_INTEGRATOR ? nop : gsl_integration_workspace_free}; #ifdef _OPENMP -#pragma omp threadprivate(tid, use_cellhist, stdrng, gslworkspace, output_file) + +#ifdef GPU_ON +#pragma omp requires unified_shared_memory +#else +#pragma omp threadprivate(stdrng, output_file, outputlinebuf, outputstartofline, timebuf) +#endif + #endif +inline void print_line_start() { + if (outputstartofline) { + const time_t now_time = time(nullptr); + strftime(outputlinebuf, 32, "%FT%TZ", gmtime_r(&now_time, &timebuf)); + output_file << outputlinebuf << " "; + } +} + +#define printout(...) \ + { \ + print_line_start(); \ + snprintf(outputlinebuf, 1024, __VA_ARGS__); \ + outputstartofline = (outputlinebuf[strlen(outputlinebuf) - 1] == '\n'); \ + output_file << outputlinebuf; \ + output_file.flush(); \ + } + #define __artis_assert(e) \ { \ const bool pass = static_cast(e); \ if (!pass) { \ - if (output_file != nullptr) { \ - (void)fprintf(output_file, "[rank %d] %s:%d: failed assertion `%s' in function %s\n", globals::rank_global, \ - __FILE__, __LINE__, #e, __PRETTY_FUNCTION__); \ + if (output_file) { \ + output_file << "\n[rank " << globals::rank_global << "] " << __FILE__ << ":" << __LINE__ \ + << ": failed assertion `" << #e << "` in function " << __PRETTY_FUNCTION__ << "\n"; \ + output_file.flush(); \ } \ - (void)fprintf(stderr, "[rank %d] %s:%d: failed assertion `%s' in function %s\n", globals::rank_global, __FILE__, \ - __LINE__, #e, __PRETTY_FUNCTION__); \ - abort(); \ + std::cerr << "\n[rank " << globals::rank_global << "] " << __FILE__ << ":" << __LINE__ << ": failed assertion `" \ + << #e << "` in function " << __PRETTY_FUNCTION__ << "\n"; \ + std::abort(); \ } \ assert(pass); \ } @@ -60,53 +118,11 @@ extern gsl_integration_workspace *gslworkspace; #if defined TESTMODE && TESTMODE #define assert_testmodeonly(e) __artis_assert(e) #else -#define assert_testmodeonly(e) \ - if (!(e)) { \ - __builtin_unreachable(); \ - } +#define assert_testmodeonly(e) (void)0 #endif -#include "artisoptions.h" -#include "globals.h" - -// #define printout(...) fprintf(output_file, __VA_ARGS__) - -template -static auto printout(const char *format, Args... args) -> int { - if (globals::startofline[tid]) { - const time_t now_time = time(nullptr); - char s[32] = ""; - struct tm buf {}; - strftime(s, 32, "%FT%TZ", gmtime_r(&now_time, &buf)); - fprintf(output_file, "%s ", s); - } - globals::startofline[tid] = (format[strlen(format) - 1] == '\n'); - return fprintf(output_file, format, args...); -} - -static auto printout(const char *format) -> int { - if (globals::startofline[tid]) { - const time_t now_time = time(nullptr); - char s[32] = ""; - struct tm buf {}; - strftime(s, 32, "%FT%TZ", gmtime_r(&now_time, &buf)); - fprintf(output_file, "%s ", s); - } - globals::startofline[tid] = (format[strlen(format) - 1] == '\n'); - return fprintf(output_file, "%s", format); -} - -static inline auto get_bflutindex(const int tempindex, const int element, const int ion, const int level, - const int phixstargetindex) -> int { - const int contindex = -1 - globals::elements[element].ions[ion].levels[level].cont_index + phixstargetindex; - - const int bflutindex = tempindex * globals::nbfcontinua + contindex; - assert_testmodeonly(bflutindex <= TABLESIZE * globals::nbfcontinua); - return bflutindex; -} - template -inline void safeadd(T &var, T val) { +inline void atomicadd(T &var, const T &val) { #ifdef _OPENMP #pragma omp atomic update var += val; @@ -125,11 +141,7 @@ inline void safeadd(T &var, T val) { #endif } -#define safeincrement(var) safeadd((var), 1) - -// #define DO_TITER - -static inline void gsl_error_handler_printout(const char *reason, const char *file, int line, int gsl_errno) { +inline void gsl_error_handler_printout(const char *reason, const char *file, int line, int gsl_errno) { if (gsl_errno != 18) // roundoff error { printout("WARNING: gsl (%s:%d): %s (Error code %d)\n", file, line, reason, gsl_errno); @@ -137,7 +149,7 @@ static inline void gsl_error_handler_printout(const char *reason, const char *fi } } -static auto fopen_required(const std::string &filename, const char *mode) -> FILE * { +[[nodiscard]] inline auto fopen_required(const std::string &filename, const char *mode) -> FILE * { // look in the data folder first const std::string datafolderfilename = "data/" + filename; if (mode[0] == 'r' && std::filesystem::exists(datafolderfilename)) { @@ -147,13 +159,13 @@ static auto fopen_required(const std::string &filename, const char *mode) -> FIL FILE *file = std::fopen(filename.c_str(), mode); if (file == nullptr) { printout("ERROR: Could not open file '%s' for mode '%s'.\n", filename.c_str(), mode); - abort(); + std::abort(); } return file; } -static auto fstream_required(const std::string &filename, std::ios_base::openmode mode) -> std::fstream { +[[nodiscard]] inline auto fstream_required(const std::string &filename, std::ios_base::openmode mode) -> std::fstream { const std::string datafolderfilename = "data/" + filename; if (mode == std::ios::in && std::filesystem::exists(datafolderfilename)) { return fstream_required(datafolderfilename, mode); @@ -161,12 +173,23 @@ static auto fstream_required(const std::string &filename, std::ios_base::openmod auto file = std::fstream(filename, mode); if (!file.is_open()) { printout("ERROR: Could not open file '%s'\n", filename.c_str()); - abort(); + std::abort(); } return file; } +#include "globals.h" -static auto get_timestep(const double time) -> int { +[[nodiscard]] inline auto get_bflutindex(const int tempindex, const int element, const int ion, const int level, + const int phixstargetindex) -> int { + const int contindex = -1 - globals::elements[element].ions[ion].levels[level].cont_index + phixstargetindex; + + const int bflutindex = tempindex * globals::nbfcontinua + contindex; + assert_testmodeonly(bflutindex >= 0); + assert_testmodeonly(bflutindex <= TABLESIZE * globals::nbfcontinua); + return bflutindex; +} + +[[nodiscard]] inline auto get_timestep(const double time) -> int { assert_always(time >= globals::tmin); assert_always(time < globals::tmax); for (int nts = 0; nts < globals::ntimesteps; nts++) { @@ -180,7 +203,7 @@ static auto get_timestep(const double time) -> int { return -1; } -inline auto get_max_threads() -> int { +[[nodiscard]] inline auto get_max_threads() -> int { #if defined _OPENMP return omp_get_max_threads(); #else @@ -188,15 +211,7 @@ inline auto get_max_threads() -> int { #endif } -inline auto get_num_threads() -> int { -#if defined _OPENMP - return omp_get_num_threads(); -#else - return 1; -#endif -} - -inline auto get_thread_num() -> int { +[[nodiscard]] inline auto get_thread_num() -> int { #if defined _OPENMP return omp_get_thread_num(); #else @@ -205,55 +220,67 @@ inline auto get_thread_num() -> int { } inline auto rng_uniform() -> float { - const auto zrand = std::generate_canonical::digits>(stdrng); - if (zrand == 1.) { - return rng_uniform(); + while (true) { + const auto zrand = std::generate_canonical::digits>(stdrng); + if (zrand != 1.) { + return zrand; + } } - return zrand; } inline auto rng_uniform_pos() -> float { - const auto zrand = std::generate_canonical::digits>(stdrng); - if (zrand <= 0.) { - return rng_uniform_pos(); + while (true) { + const auto zrand = rng_uniform(); + if (zrand > 0) { + return zrand; + } } - return zrand; } -inline void rng_init(const uint_fast64_t zseed) { - printout("rng is a std::mt19937 generator\n"); - stdrng.seed(zseed); -} - -inline auto is_pid_running(pid_t pid) -> bool { +[[nodiscard]] inline auto is_pid_running(pid_t pid) -> bool { while (waitpid(-1, nullptr, WNOHANG) > 0) { // Wait for defunct.... } - return (0 == kill(pid, 0)); + return (kill(pid, 0) == 0); } inline void check_already_running() { - pid_t artispid = getpid(); + if (globals::rank_global == 0) { + const pid_t artispid = getpid(); + + if (std::filesystem::exists("artis.pid")) { + auto pidfile = std::fstream("artis.pid", std::ios::in); + pid_t artispid_in = 0; + pidfile >> artispid_in; + pidfile.close(); + if (is_pid_running(artispid_in)) { + fprintf(stderr, + "\nERROR: artis or exspec is already running in this folder with existing pid %d. Refusing to start. " + "(delete artis.pid if you are sure this is incorrect)\n", + artispid_in); + std::abort(); + } + } - if (std::filesystem::exists("artis.pid")) { - auto pidfile = std::fstream("artis.pid", std::ios::in); - pid_t artispid_in = 0; - pidfile >> artispid_in; + auto pidfile = std::fstream("artis.pid", std::ofstream::out | std::ofstream::trunc); + pidfile << artispid; pidfile.close(); - if (is_pid_running(artispid_in)) { - fprintf( - stderr, - "\nERROR: artis or exspec is already running in this folder with existing pid %d. Refusing to start. (delete " - "artis.pid if you are sure this is incorrect)\n", - artispid_in); - abort(); - } } - auto pidfile = std::fstream("artis.pid", std::ofstream::out | std::ofstream::trunc); - pidfile << artispid; - pidfile.close(); +// make sure rank 0 checked for a pid file before we proceed +#ifdef MPI_ON + MPI_Barrier(MPI_COMM_WORLD); +#endif +} + +constexpr auto get_range_chunk(int size, int nchunks, int nchunk) -> std::tuple { + const int minchunksize = size / nchunks; // integer division, minimum non-empty cells per process + const int n_remainder = size % nchunks; + const auto nstart = + (minchunksize + 1) * std::min(n_remainder, nchunk) + minchunksize * std::max(0, nchunk - n_remainder); + const auto nsize = (nchunk < n_remainder) ? minchunksize + 1 : minchunksize; + return {nstart, nsize}; } #endif // SN3D_H diff --git a/spectrum.h b/spectrum.h deleted file mode 100644 index 24a5a7f2f..000000000 --- a/spectrum.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef SPECTRUM_H -#define SPECTRUM_H - -#include -#include -#include - -struct timestepspec { - double *flux = nullptr; - double *absorption = nullptr; - double *emission = nullptr; - double *trueemission = nullptr; -}; - -struct spec { - double nu_min = -1.; - double nu_max = -1.; - 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 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, const struct spec *stokes_i, const struct spec *stokes_q, - const 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); - -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); - -#endif // SPECTRUM_H diff --git a/spectrum.cc b/spectrum_lightcurve.cc similarity index 76% rename from spectrum.cc rename to spectrum_lightcurve.cc index 1417121f9..8befbfdec 100644 --- a/spectrum.cc +++ b/spectrum_lightcurve.cc @@ -1,17 +1,28 @@ -#include "spectrum.h" +#include "spectrum_lightcurve.h" + +#ifdef MPI_ON +#include +#endif #include #include +#include #include -#include +#include +#include #include +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "exspec.h" -#include "light_curve.h" +#include "globals.h" +#include "packet.h" #include "sn3d.h" #include "vectors.h" +namespace { + bool TRACE_EMISSION_ABSORPTION_REGION_ON = false; constexpr double traceemissabs_lambdamin = 1000.; // in Angstroms @@ -29,13 +40,13 @@ using emissionabsorptioncontrib = struct emissionabsorptioncontrib { int lineindex; // this will be important when the list gets sorted }; -static std::vector traceemissionabsorption; +std::vector traceemissionabsorption; double traceemission_totalenergy = 0.; double traceabsorption_totalenergy = 0.; -struct spec rpkt_spectra; +Spectra rpkt_spectra; -static void printout_tracemission_stats() { +void printout_tracemission_stats() { const int maxlinesprinted = 500; // mode is 0 for emission and 1 for absorption @@ -62,11 +73,11 @@ static void printout_tracemission_stats() { if (globals::nlines > maxlinesprinted) { nlines_limited = maxlinesprinted; } - printout("%17s %4s %9s %5s %5s %8s %8s %4s %7s %7s %7s %7s\n", "energy", "Z", "ion_stage", "upper", "lower", + printout("%17s %4s %9s %5s %5s %8s %8s %4s %7s %7s %7s %7s\n", "energy", "Z", "ionstage", "upper", "lower", "coll_str", "A", "forb", "lambda", "", "B_lu", "B_ul"); for (int i = 0; i < nlines_limited; i++) { - double encontrib = NAN; - double totalenergy = NAN; + double encontrib{NAN}; + double totalenergy{NAN}; if (mode == 0) { encontrib = traceemissionabsorption[i].energyemitted; totalenergy = traceemission_totalenergy; @@ -81,7 +92,7 @@ static void printout_tracemission_stats() { const int ion = globals::linelist[lineindex].ionindex; const double linelambda = 1e8 * CLIGHT / globals::linelist[lineindex].nu; // flux-weighted average radial velocity of emission in km/s - double v_rad = NAN; + double v_rad{NAN}; if (mode == 0) { v_rad = traceemissionabsorption[i].emission_weightedvelocity_sum / traceemissionabsorption[i].energyemitted / 1e5; @@ -122,20 +133,194 @@ static void printout_tracemission_stats() { traceemissionabsorption.clear(); } -static auto get_proccount() -> int +auto get_proccount() -> int // number of different emission processes (bf and bb for each ion, and free-free) { return 2 * get_nelements() * get_max_nions() + 1; } +auto columnindex_from_emissiontype(const int et) -> int { + if (et >= 0) { + /// bb-emission + const int element = globals::linelist[et].elementindex; + const int ion = globals::linelist[et].ionindex; + return element * get_max_nions() + ion; + } + if (et == EMTYPE_FREEFREE) { + /// ff-emission + + const int contindex = -1 - et; + assert_always(contindex >= globals::nbfcontinua); // make sure the special value didn't collide with a real process + + return 2 * get_nelements() * get_max_nions(); + } + if (et == EMTYPE_NOTSET) { + return -1; + } /// bf-emission + const int contindex = -1 - et; + if (globals::nbfcontinua == 0) { + // assert_always(false); // if there are no bf processes, we should not get here + return 2 * get_nelements() * get_max_nions(); + } + assert_always(contindex < globals::nbfcontinua); + const int element = globals::bflist[contindex].elementindex; + const int ion = globals::bflist[contindex].ionindex; + const int level = globals::bflist[contindex].levelindex; + const int phixstargetindex = globals::bflist[contindex].phixstargetindex; + const int upperionlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); + + assert_always(get_emtype_continuum(element, ion, level, upperionlevel) == et); + + return get_nelements() * get_max_nions() + element * get_max_nions() + ion; +} + +void add_to_spec(const Packet &pkt, const int current_abin, Spectra &spectra, const Spectra *stokes_i, + const Spectra *stokes_q, const Spectra *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. + + // specific angle bins contain fewer packets than the full sphere, so must be normalised to match + const double anglefactor = (current_abin >= 0) ? MABINS : 1.; + + const double nu_min = spectra.nu_min; + const double nu_max = spectra.nu_max; + const double t_arrive = get_arrive_time(pkt); + if (t_arrive > globals::tmin && t_arrive < globals::tmax && pkt.nu_rf > nu_min && pkt.nu_rf < nu_max) { + const int nt = get_timestep(t_arrive); + const double dlognu = (log(nu_max) - log(nu_min)) / MNUBINS; + + const int nnu = static_cast((log(pkt.nu_rf) - log(nu_min)) / dlognu); + assert_always(nnu < MNUBINS); + + const double deltaE = pkt.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; + + if (stokes_i != nullptr) { + stokes_i->timesteps[nt].flux[nnu] += pkt.stokes[0] * deltaE; + } + if (stokes_q != nullptr) { + stokes_q->timesteps[nt].flux[nnu] += pkt.stokes[1] * deltaE; + } + if (stokes_u != nullptr) { + stokes_u->timesteps[nt].flux[nnu] += pkt.stokes[2] * deltaE; + } + + if (spectra.do_emission_res) { + const int proccount = get_proccount(); + + const int truenproc = columnindex_from_emissiontype(pkt.trueemissiontype); + assert_always(truenproc < proccount); + if (truenproc >= 0) { + spectra.timesteps[nt].trueemission[nnu * proccount + truenproc] += deltaE; + } + + const int nproc = columnindex_from_emissiontype(pkt.emissiontype); + assert_always(nproc < proccount); + if (nproc >= 0) { // -1 means not set + spectra.timesteps[nt].emission[nnu * proccount + nproc] += deltaE; + + if (stokes_i != nullptr && stokes_i->do_emission_res) { + stokes_i->timesteps[nt].emission[nnu * proccount + nproc] += pkt.stokes[0] * deltaE; + } + if (stokes_q != nullptr && stokes_q->do_emission_res) { + stokes_q->timesteps[nt].emission[nnu * proccount + nproc] += pkt.stokes[1] * deltaE; + } + if (stokes_u != nullptr && stokes_u->do_emission_res) { + stokes_u->timesteps[nt].emission[nnu * proccount + nproc] += pkt.stokes[2] * deltaE; + } + } + + if (TRACE_EMISSION_ABSORPTION_REGION_ON && (current_abin == -1)) { + const int et = pkt.trueemissiontype; + if (et >= 0) { + if (t_arrive >= traceemissabs_timemin && t_arrive <= traceemissabs_timemax) { + if (pkt.nu_rf >= traceemissabs_nulower && pkt.nu_rf <= traceemissabs_nuupper) { + traceemissionabsorption[et].energyemitted += deltaE; + + traceemissionabsorption[et].emission_weightedvelocity_sum += pkt.trueemissionvelocity * deltaE; + + traceemission_totalenergy += deltaE; + } + } + } + } + + const int nnu_abs = (pkt.absorptionfreq > 0 && std::isfinite(pkt.absorptionfreq)) + ? static_cast((log(pkt.absorptionfreq) - log(nu_min)) / dlognu) + : -1; + if (nnu_abs >= 0 && nnu_abs < MNUBINS) { + const int ioncount = get_nelements() * get_max_nions(); + const double deltaE_absorption = pkt.e_rf / globals::timesteps[nt].width / spectra.delta_freq[nnu_abs] / 4.e12 / + PI / PARSEC / PARSEC / globals::nprocs_exspec * anglefactor; + const int at = pkt.absorptiontype; + if (at >= 0) { + /// bb-emission + const int element = globals::linelist[at].elementindex; + const int ion = globals::linelist[at].ionindex; + spectra.timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += deltaE_absorption; + + if (stokes_i != nullptr && stokes_i->do_emission_res) { + stokes_i->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += + pkt.stokes[0] * deltaE_absorption; + } + if (stokes_q != nullptr && stokes_q->do_emission_res) { + stokes_q->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += + pkt.stokes[1] * deltaE_absorption; + } + if (stokes_u != nullptr && stokes_u->do_emission_res) { + stokes_u->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += + pkt.stokes[2] * deltaE_absorption; + } + + if (TRACE_EMISSION_ABSORPTION_REGION_ON && t_arrive >= traceemissabs_timemin && + t_arrive <= traceemissabs_timemax) { + if ((current_abin == -1) && (pkt.nu_rf >= traceemissabs_nulower) && (pkt.nu_rf <= traceemissabs_nuupper)) { + traceemissionabsorption[at].energyabsorbed += deltaE_absorption; + + const auto vel_vec = get_velocity(pkt.em_pos, pkt.em_time); + traceemissionabsorption[at].absorption_weightedvelocity_sum += vec_len(vel_vec) * deltaE_absorption; + + traceabsorption_totalenergy += deltaE_absorption; + } + } + } + } + } + } +} + +#ifdef MPI_ON +void mpi_reduce_spectra(int my_rank, Spectra &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); + + 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, + 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, + 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, + MNUBINS * proccount, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); + } + } +} +#endif + +} // anonymous namespace + 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) { + const Spectra &spectra, int numtimesteps) { FILE *spec_file = fopen_required(spec_filename, "w"); - FILE *emission_file = nullptr; - FILE *trueemission_file = nullptr; - FILE *absorption_file = nullptr; + FILE *emission_file{}; + FILE *trueemission_file{}; + FILE *absorption_file{}; const bool do_emission_res = spectra.do_emission_res; @@ -200,11 +385,11 @@ void write_spectrum(const std::string &spec_filename, const std::string &emissio } void write_specpol(const std::string &specpol_filename, const std::string &emission_filename, - const std::string &absorption_filename, const struct spec *stokes_i, const struct spec *stokes_q, - const struct spec *stokes_u) { + const std::string &absorption_filename, const Spectra *stokes_i, const Spectra *stokes_q, + const Spectra *stokes_u) { FILE *specpol_file = fopen_required(specpol_filename, "w"); - FILE *emissionpol_file = nullptr; - FILE *absorptionpol_file = nullptr; + FILE *emissionpol_file{}; + FILE *absorptionpol_file{}; const bool do_emission_res = stokes_i->do_emission_res; @@ -294,159 +479,6 @@ void write_specpol(const std::string &specpol_filename, const std::string &emiss } } -static auto columnindex_from_emissiontype(const int et) -> int { - if (et >= 0) { - /// bb-emission - const int element = globals::linelist[et].elementindex; - const int ion = globals::linelist[et].ionindex; - return element * get_max_nions() + ion; - } - if (et == EMTYPE_FREEFREE) { - /// ff-emission - - const int contindex = -1 - et; - assert_always(contindex >= globals::nbfcontinua); // make sure the special value didn't collide with a real process - - return 2 * get_nelements() * get_max_nions(); - } - if (et == EMTYPE_NOTSET) { - return -1; - } /// bf-emission - const int contindex = -1 - et; - if (globals::nbfcontinua == 0) { - // assert_always(false); // if there are no bf processes, we should not get here - return 2 * get_nelements() * get_max_nions(); - } - assert_always(contindex < globals::nbfcontinua); - const int element = globals::bflist[contindex].elementindex; - const int ion = globals::bflist[contindex].ionindex; - const int level = globals::bflist[contindex].levelindex; - const int phixstargetindex = globals::bflist[contindex].phixstargetindex; - const int upperionlevel = get_phixsupperlevel(element, ion, level, phixstargetindex); - - assert_always(get_continuumindex(element, ion, level, upperionlevel) == et); - - return get_nelements() * get_max_nions() + element * get_max_nions() + ion; -} - -static void add_to_spec(const struct packet *const pkt_ptr, const 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. - - // specific angle bins contain fewer packets than the full sphere, so must be normalised to match - const double anglefactor = (current_abin >= 0) ? MABINS : 1.; - - const double nu_min = spectra.nu_min; - const double nu_max = spectra.nu_max; - const double t_arrive = get_arrive_time(pkt_ptr); - if (t_arrive > globals::tmin && t_arrive < globals::tmax && pkt_ptr->nu_rf > nu_min && pkt_ptr->nu_rf < nu_max) { - const int nt = get_timestep(t_arrive); - const double dlognu = (log(nu_max) - log(nu_min)) / MNUBINS; - - 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::timesteps[nt].width / spectra.delta_freq[nnu] / 4.e12 / PI / PARSEC / - PARSEC / globals::nprocs_exspec * anglefactor; - - spectra.timesteps[nt].flux[nnu] += deltaE; - - if (stokes_i != nullptr) { - stokes_i->timesteps[nt].flux[nnu] += pkt_ptr->stokes[0] * deltaE; - } - if (stokes_q != nullptr) { - stokes_q->timesteps[nt].flux[nnu] += pkt_ptr->stokes[1] * deltaE; - } - if (stokes_u != nullptr) { - stokes_u->timesteps[nt].flux[nnu] += pkt_ptr->stokes[2] * deltaE; - } - - if (spectra.do_emission_res) { - const int proccount = get_proccount(); - - const int truenproc = columnindex_from_emissiontype(pkt_ptr->trueemissiontype); - assert_always(truenproc < proccount); - if (truenproc >= 0) { - spectra.timesteps[nt].trueemission[nnu * proccount + truenproc] += deltaE; - } - - const int nproc = columnindex_from_emissiontype(pkt_ptr->emissiontype); - assert_always(nproc < proccount); - if (nproc >= 0) { // -1 means not set - spectra.timesteps[nt].emission[nnu * proccount + nproc] += deltaE; - - if (stokes_i != nullptr && stokes_i->do_emission_res) { - stokes_i->timesteps[nt].emission[nnu * proccount + nproc] += pkt_ptr->stokes[0] * deltaE; - } - if (stokes_q != nullptr && stokes_q->do_emission_res) { - stokes_q->timesteps[nt].emission[nnu * proccount + nproc] += pkt_ptr->stokes[1] * deltaE; - } - if (stokes_u != nullptr && stokes_u->do_emission_res) { - stokes_u->timesteps[nt].emission[nnu * proccount + nproc] += pkt_ptr->stokes[2] * deltaE; - } - } - - if (TRACE_EMISSION_ABSORPTION_REGION_ON && (current_abin == -1)) { - const int et = pkt_ptr->trueemissiontype; - if (et >= 0) { - if (t_arrive >= traceemissabs_timemin && t_arrive <= traceemissabs_timemax) { - if (pkt_ptr->nu_rf >= traceemissabs_nulower && pkt_ptr->nu_rf <= traceemissabs_nuupper) { - traceemissionabsorption[et].energyemitted += deltaE; - - traceemissionabsorption[et].emission_weightedvelocity_sum += pkt_ptr->trueemissionvelocity * deltaE; - - traceemission_totalenergy += deltaE; - } - } - } - } - - 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::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 - const int element = globals::linelist[at].elementindex; - const int ion = globals::linelist[at].ionindex; - spectra.timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += deltaE_absorption; - - if (stokes_i != nullptr && stokes_i->do_emission_res) { - stokes_i->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += - pkt_ptr->stokes[0] * deltaE_absorption; - } - if (stokes_q != nullptr && stokes_q->do_emission_res) { - stokes_q->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += - pkt_ptr->stokes[1] * deltaE_absorption; - } - if (stokes_u != nullptr && stokes_u->do_emission_res) { - stokes_u->timesteps[nt].absorption[nnu_abs * ioncount + element * get_max_nions() + ion] += - pkt_ptr->stokes[2] * deltaE_absorption; - } - - if (TRACE_EMISSION_ABSORPTION_REGION_ON && t_arrive >= traceemissabs_timemin && - t_arrive <= traceemissabs_timemax) { - if ((current_abin == -1) && (pkt_ptr->nu_rf >= traceemissabs_nulower) && - (pkt_ptr->nu_rf <= traceemissabs_nuupper)) { - traceemissionabsorption[at].energyabsorbed += deltaE_absorption; - - double vel_vec[3]; - get_velocity(pkt_ptr->em_pos, vel_vec, pkt_ptr->em_time); - traceemissionabsorption[at].absorption_weightedvelocity_sum += vec_len(vel_vec) * deltaE_absorption; - - traceabsorption_totalenergy += deltaE_absorption; - } - } - } - } - } - } -} - void init_spectrum_trace() { if (TRACE_EMISSION_ABSORPTION_REGION_ON) { traceemission_totalenergy = 0.; @@ -462,7 +494,7 @@ void init_spectrum_trace() { } } -void init_spectra(struct spec &spectra, const double nu_min, const double nu_max, const bool do_emission_res) { +void init_spectra(Spectra &spectra, const double nu_min, const double nu_max, const bool do_emission_res) { // 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. @@ -490,8 +522,8 @@ void init_spectra(struct spec &spectra, const double nu_min, const double nu_max spectra.fluxalltimesteps.resize(globals::ntimesteps * MNUBINS); std::ranges::fill(spectra.fluxalltimesteps, 0.0); - mem_usage += globals::ntimesteps * sizeof(struct spec); - mem_usage += globals::ntimesteps * sizeof(struct timestepspec); + mem_usage += globals::ntimesteps * sizeof(Spectra); + mem_usage += globals::ntimesteps * sizeof(TimeStepstepspec); mem_usage += globals::ntimesteps * MNUBINS * sizeof(double); for (int nts = 0; nts < globals::ntimesteps; nts++) { @@ -540,8 +572,8 @@ void init_spectra(struct spec &spectra, const double nu_min, const double nu_max } } -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) +void add_to_spec_res(const Packet &pkt, int current_abin, Spectra &spectra, const Spectra *stokes_i, + const Spectra *stokes_q, const Spectra *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. @@ -550,33 +582,14 @@ void add_to_spec_res(const struct packet *const pkt_ptr, int current_abin, struc // 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). - if (current_abin == -1 || get_escapedirectionbin(pkt_ptr->dir, globals::syn_dir) == current_abin) { + if (current_abin == -1 || get_escapedirectionbin(pkt.dir, globals::syn_dir) == current_abin) { // either angle average spectrum or packet matches the selected angle bin - add_to_spec(pkt_ptr, current_abin, spectra, stokes_i, stokes_q, stokes_u); - } -} - -#ifdef MPI_ON -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); - - 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, - 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, - 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, - MNUBINS * proccount, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); - } + add_to_spec(pkt, current_abin, spectra, stokes_i, stokes_q, stokes_u); } } -#endif -void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) { - const time_t time_func_start = time(nullptr); +void write_partial_lightcurve_spectra(int my_rank, int nts, Packet *pkts) { + const auto time_func_start = std::time(nullptr); std::vector rpkt_light_curve_lum(globals::ntimesteps, 0.); std::vector rpkt_light_curve_lumcmf(globals::ntimesteps, 0.); @@ -600,10 +613,10 @@ void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) 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, rpkt_light_curve_lumcmf); - add_to_spec_res(&pkts[ii], abin, rpkt_spectra, nullptr, nullptr, nullptr); + 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, gamma_light_curve_lumcmf); + add_to_lc_res(pkts[ii], abin, gamma_light_curve_lum, gamma_light_curve_lumcmf); } } } @@ -611,7 +624,7 @@ void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) const int numtimesteps = nts + 1; // only produce spectra and light curves up to one past nts assert_always(numtimesteps <= globals::ntimesteps); - const time_t time_mpireduction_start = time(nullptr); + const auto time_mpireduction_start = std::time(nullptr); #ifdef MPI_ON MPI_Barrier(MPI_COMM_WORLD); mpi_reduce_spectra(my_rank, rpkt_spectra, numtimesteps); @@ -625,7 +638,7 @@ void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) numtimesteps, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); MPI_Barrier(MPI_COMM_WORLD); #endif - const time_t time_mpireduction_end = time(nullptr); + const auto time_mpireduction_end = std::time(nullptr); if (my_rank == 0) { write_light_curve("light_curve.out", -1, rpkt_light_curve_lum, rpkt_light_curve_lumcmf, numtimesteps); @@ -638,6 +651,78 @@ void write_partial_lightcurve_spectra(int my_rank, int nts, struct packet *pkts) #endif printout("timestep %d: Saving partial light curves and %sspectra took %lds (%lds for MPI reduction)\n", nts, - do_emission_res ? "emission/absorption " : "", time(nullptr) - time_func_start, + do_emission_res ? "emission/absorption " : "", std::time(nullptr) - time_func_start, time_mpireduction_end - time_mpireduction_start); +} + +// 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 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) { + printout("failed to open %s\n", lc_filename.c_str()); + std::abort(); + } + + printout("Writing %s\n", lc_filename.c_str()); + + constexpr int maxlen = 1024; + char linebuffer[maxlen]; + + /// Print out the UVOIR bolometric light curve. + for (int nts = 0; nts < numtimesteps; nts++) { + 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'; + } + + 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::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 Packet &pkt, 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) { + /// Put this into the time grid + const double arrive_time = get_arrive_time(pkt); + if (arrive_time > globals::tmin && arrive_time < globals::tmax) { + const int nt = get_timestep(arrive_time); + atomicadd(light_curve_lum[nt], pkt.e_rf / globals::timesteps[nt].width / globals::nprocs_exspec); + } + + const double inverse_gamma = std::sqrt(1. - (globals::vmax * globals::vmax / CLIGHTSQUARED)); + + /// Now do the cmf light curve. + // t_arrive = pkt.escape_time * sqrt(1. - (vmax*vmax/CLIGHTSQUARED)); + const double arrive_time_cmf = pkt.escape_time * inverse_gamma; + + if (arrive_time_cmf > globals::tmin && arrive_time_cmf < globals::tmax) { + const int nt = get_timestep(arrive_time_cmf); + atomicadd(light_curve_lumcmf[nt], + pkt.e_cmf / globals::timesteps[nt].width / globals::nprocs_exspec / inverse_gamma); + } + + return; + } + if (get_escapedirectionbin(pkt.dir, globals::syn_dir) == current_abin) { + // Add only packets which escape to the current angle bin + const double t_arrive = get_arrive_time(pkt); + if (t_arrive > globals::tmin && t_arrive < globals::tmax) { + const int nt = get_timestep(t_arrive); + atomicadd(light_curve_lum[nt], pkt.e_rf / globals::timesteps[nt].width * MABINS / globals::nprocs_exspec); + } + } } \ No newline at end of file diff --git a/spectrum_lightcurve.h b/spectrum_lightcurve.h new file mode 100644 index 000000000..7b2218a81 --- /dev/null +++ b/spectrum_lightcurve.h @@ -0,0 +1,51 @@ +#pragma once +#ifndef SPECTRUM_H +#define SPECTRUM_H + +#include +#include + +#include "packet.h" + +struct TimeStepstepspec { + double *flux{}; + double *absorption{}; + double *emission{}; + double *trueemission{}; +}; + +struct Spectra { + double nu_min = -1.; + double nu_max = -1.; + 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 std::string &emission_filename, + const std::string &trueemission_filename, const std::string &absorption_filename, + const Spectra &spectra, int numtimesteps); + +void write_specpol(const std::string &specpol_filename, const std::string &emission_filename, + const std::string &absorption_filename, const Spectra *stokes_i, const Spectra *stokes_q, + const Spectra *stokes_u); + +void add_to_spec_res(const Packet &pkt, int current_abin, Spectra &spectra, const Spectra *stokes_i, + const Spectra *stokes_q, const Spectra *stokes_u); + +void init_spectra(Spectra &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, Packet *pkts); + +void add_to_lc_res(const Packet &pkt, 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 std::vector &light_curve_lum, + const std::vector &light_curve_lumcmf, int numtimesteps); + +#endif // SPECTRUM_H diff --git a/stats.cc b/stats.cc index 8ba380327..78a8504d6 100644 --- a/stats.cc +++ b/stats.cc @@ -1,39 +1,42 @@ #include "stats.h" -#include +#ifdef MPI_ON +#include +#endif +#include +#include +#include +#include +#include + +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" #include "globals.h" #include "grid.h" #include "ltepop.h" #include "nonthermal.h" +#include "packet.h" #include "sn3d.h" namespace stats { -static double *ionstats = nullptr; -static std::vector> eventstats; +namespace { -void init() { - eventstats.resize(get_max_threads(), {0}); +std::vector ionstats; +std::array eventstats{}; - if constexpr (TRACK_ION_STATS) { - ionstats = - static_cast(malloc(grid::get_npts_model() * get_includedions() * ION_STAT_COUNT * sizeof(double))); - } -} +} // anonymous namespace -void cleanup() { +void init() { if constexpr (TRACK_ION_STATS) { - free(ionstats); + ionstats.resize(grid::get_npts_model() * get_includedions() * ION_STAT_COUNT, 0.); } } void increment_ion_stats(const int modelgridindex, const int element, const int ion, enum ionstattypes ionstattype, const double increment) { - if constexpr (!TRACK_ION_MASTATS) { - return; - } if (ionstattype >= 18) { return; } @@ -42,18 +45,17 @@ void increment_ion_stats(const int modelgridindex, const int element, const int assert_testmodeonly(ionstattype < ION_STAT_COUNT); const int uniqueionindex = get_uniqueionindex(element, ion); - safeadd( + atomicadd( ionstats[modelgridindex * get_includedions() * ION_STAT_COUNT + uniqueionindex * ION_STAT_COUNT + ionstattype], increment); } -void increment_ion_stats_contabsorption(const struct packet *const pkt_ptr, const int modelgridindex, const int element, - const int ion) { - const double n_photons_absorbed = pkt_ptr->e_cmf / H / pkt_ptr->nu_cmf; +void increment_ion_stats_contabsorption(const Packet &pkt, const int modelgridindex, const int element, const int ion) { + const double n_photons_absorbed = pkt.e_cmf / H / pkt.nu_cmf; stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_PHOTOION, n_photons_absorbed); - const int et = pkt_ptr->emissiontype; + const int et = pkt.emissiontype; if (et >= 0) // r-packet is from bound-bound emission { stats::increment_ion_stats(modelgridindex, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUND, n_photons_absorbed); @@ -160,35 +162,27 @@ 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); - eventstats[tid][i]++; + atomicadd(eventstats[i], static_cast(1)); } void pkt_action_counters_reset() { for (int i = 0; i < COUNTER_COUNT; i++) { - eventstats[tid][i] = 0; + eventstats[i] = 0; } nonthermal::nt_reset_stats(); globals::nesc = 0; } -auto get_counter(enum eventcounters i) -> int { - assert_always(i < COUNTER_COUNT); - int count = 0; - for (int t = 0; t < get_num_threads(); t++) { - count += eventstats[t][i]; - } - return count; +auto get_counter(enum eventcounters i) -> ptrdiff_t { + assert_testmodeonly(i >= 0); + assert_testmodeonly(i < COUNTER_COUNT); + return eventstats[i]; } -void pkt_action_counters_printout(const struct packet *const pkt, const int nts) { - u_int64_t allpktinteractions = 0; - for (int i = 0; i < globals::npkts; i++) { - assert_always(pkt[i].interactions >= 0); - allpktinteractions += pkt[i].interactions; - } - const double meaninteractions = static_cast(allpktinteractions) / globals::npkts; - printout("mean number of interactions per packet = %g\n", meaninteractions); +void pkt_action_counters_printout(const int nts) { + const double meaninteractions = static_cast(get_counter(COUNTER_INTERACTIONS)) / globals::npkts; + printout("timestep %d: mean number of interactions per packet = %g\n", nts, meaninteractions); const double deltat = globals::timesteps[nts].width; double modelvolume = 0.; @@ -197,50 +191,52 @@ void pkt_action_counters_printout(const struct packet *const pkt, const int nts) } /// Printout packet statistics - printout("ma_stat_activation_collexc = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_COLLEXC)); - printout("ma_stat_activation_collion = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_COLLION)); - printout("ma_stat_activation_ntcollexc = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_NTCOLLEXC)); - printout("ma_stat_activation_ntcollion = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_NTCOLLION)); - printout("ma_stat_activation_bb = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_BB)); - printout("ma_stat_activation_bf = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_BF)); - printout("ma_stat_activation_fb = %d\n", get_counter(COUNTER_MA_STAT_ACTIVATION_FB)); - printout("ma_stat_deactivation_colldeexc = %d\n", get_counter(COUNTER_MA_STAT_DEACTIVATION_COLLDEEXC)); - printout("ma_stat_deactivation_collrecomb = %d\n", get_counter(COUNTER_MA_STAT_DEACTIVATION_COLLRECOMB)); - printout("ma_stat_deactivation_bb = %d\n", get_counter(COUNTER_MA_STAT_DEACTIVATION_BB)); - printout("ma_stat_deactivation_fb = %d\n", get_counter(COUNTER_MA_STAT_DEACTIVATION_FB)); - printout("ma_stat_internaluphigher = %d\n", get_counter(COUNTER_MA_STAT_INTERNALUPHIGHER)); - printout("ma_stat_internaluphighernt = %d\n", get_counter(COUNTER_MA_STAT_INTERNALUPHIGHERNT)); - printout("ma_stat_internaldownlower = %d\n", get_counter(COUNTER_MA_STAT_INTERNALDOWNLOWER)); - - printout("k_stat_to_ma_collexc = %d\n", get_counter(COUNTER_K_STAT_TO_MA_COLLEXC)); - printout("k_stat_to_ma_collion = %d\n", get_counter(COUNTER_K_STAT_TO_MA_COLLION)); - printout("k_stat_to_r_ff = %d\n", get_counter(COUNTER_K_STAT_TO_R_FF)); - printout("k_stat_to_r_fb = %d\n", get_counter(COUNTER_K_STAT_TO_R_FB)); - printout("k_stat_to_r_bb = %d\n", get_counter(COUNTER_K_STAT_TO_R_BB)); - printout("k_stat_from_ff = %d\n", get_counter(COUNTER_K_STAT_FROM_FF)); - printout("k_stat_from_bf = %d\n", get_counter(COUNTER_K_STAT_FROM_BF)); - printout("k_stat_from_earlierdecay = %d\n", get_counter(COUNTER_K_STAT_FROM_EARLIERDECAY)); - - printout("nt_stat_from_gamma = %d\n", get_counter(COUNTER_NT_STAT_FROM_GAMMA)); - printout("nt_stat_to_ionization = %d\n", get_counter(COUNTER_NT_STAT_TO_IONIZATION)); - printout("nt_stat_to_excitation = %d\n", get_counter(COUNTER_NT_STAT_TO_EXCITATION)); - printout("nt_stat_to_kpkt = %d\n", get_counter(COUNTER_NT_STAT_TO_KPKT)); + printout("timestep %d: ma_stat_activation_collexc = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_COLLEXC)); + printout("timestep %d: ma_stat_activation_collion = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_COLLION)); + printout("timestep %d: ma_stat_activation_ntcollexc = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_NTCOLLEXC)); + printout("timestep %d: ma_stat_activation_ntcollion = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_NTCOLLION)); + printout("timestep %d: ma_stat_activation_bb = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_BB)); + printout("timestep %d: ma_stat_activation_bf = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_BF)); + printout("timestep %d: ma_stat_activation_fb = %td\n", nts, get_counter(COUNTER_MA_STAT_ACTIVATION_FB)); + printout("timestep %d: ma_stat_deactivation_colldeexc = %td\n", nts, + get_counter(COUNTER_MA_STAT_DEACTIVATION_COLLDEEXC)); + printout("timestep %d: ma_stat_deactivation_collrecomb = %td\n", nts, + get_counter(COUNTER_MA_STAT_DEACTIVATION_COLLRECOMB)); + printout("timestep %d: ma_stat_deactivation_bb = %td\n", nts, get_counter(COUNTER_MA_STAT_DEACTIVATION_BB)); + printout("timestep %d: ma_stat_deactivation_fb = %td\n", nts, get_counter(COUNTER_MA_STAT_DEACTIVATION_FB)); + printout("timestep %d: ma_stat_internaluphigher = %td\n", nts, get_counter(COUNTER_MA_STAT_INTERNALUPHIGHER)); + printout("timestep %d: ma_stat_internaluphighernt = %td\n", nts, get_counter(COUNTER_MA_STAT_INTERNALUPHIGHERNT)); + printout("timestep %d: ma_stat_internaldownlower = %td\n", nts, get_counter(COUNTER_MA_STAT_INTERNALDOWNLOWER)); + + printout("timestep %d: k_stat_to_ma_collexc = %td\n", nts, get_counter(COUNTER_K_STAT_TO_MA_COLLEXC)); + printout("timestep %d: k_stat_to_ma_collion = %td\n", nts, get_counter(COUNTER_K_STAT_TO_MA_COLLION)); + printout("timestep %d: k_stat_to_r_ff = %td\n", nts, get_counter(COUNTER_K_STAT_TO_R_FF)); + printout("timestep %d: k_stat_to_r_fb = %td\n", nts, get_counter(COUNTER_K_STAT_TO_R_FB)); + printout("timestep %d: k_stat_to_r_bb = %td\n", nts, get_counter(COUNTER_K_STAT_TO_R_BB)); + printout("timestep %d: k_stat_from_ff = %td\n", nts, get_counter(COUNTER_K_STAT_FROM_FF)); + printout("timestep %d: k_stat_from_bf = %td\n", nts, get_counter(COUNTER_K_STAT_FROM_BF)); + printout("timestep %d: k_stat_from_earlierdecay = %td\n", nts, get_counter(COUNTER_K_STAT_FROM_EARLIERDECAY)); + + printout("timestep %d: nt_stat_from_gamma = %td\n", nts, get_counter(COUNTER_NT_STAT_FROM_GAMMA)); + printout("timestep %d: nt_stat_to_ionization = %td\n", nts, get_counter(COUNTER_NT_STAT_TO_IONIZATION)); + printout("timestep %d: nt_stat_to_excitation = %td\n", nts, get_counter(COUNTER_NT_STAT_TO_EXCITATION)); + printout("timestep %d: nt_stat_to_kpkt = %td\n", nts, get_counter(COUNTER_NT_STAT_TO_KPKT)); nonthermal::nt_print_stats(modelvolume, deltat); - printout("escounter = %d\n", get_counter(COUNTER_ESCOUNTER)); - printout("cellcrossing = %d\n", get_counter(COUNTER_CELLCROSSINGS)); - printout("updatecellcounter = %d\n", get_counter(COUNTER_UPDATECELL)); - printout("coolingratecalccounter = %d\n", get_counter(COUNTER_COOLINGRATECALCCOUNTER)); - printout("resonancescatterings = %d\n", get_counter(COUNTER_RESONANCESCATTERINGS)); + printout("timestep %d: escounter = %td\n", nts, get_counter(COUNTER_ESCOUNTER)); + printout("timestep %d: cellcrossing = %td\n", nts, get_counter(COUNTER_CELLCROSSINGS)); + printout("timestep %d: updatecellcounter = %td\n", nts, get_counter(COUNTER_UPDATECELL)); + printout("timestep %d: resonancescatterings = %td\n", nts, get_counter(COUNTER_RESONANCESCATTERINGS)); - printout("upscatterings = %d\n", get_counter(COUNTER_UPSCATTER)); - printout("downscatterings = %d\n", get_counter(COUNTER_DOWNSCATTER)); + printout("timestep %d: upscatterings = %td\n", nts, get_counter(COUNTER_UPSCATTER)); + printout("timestep %d: downscatterings = %td\n", nts, get_counter(COUNTER_DOWNSCATTER)); } void reduce_estimators() { #ifdef MPI_ON - MPI_Allreduce(MPI_IN_PLACE, stats::ionstats, grid::get_npts_model() * get_includedions() * stats::ION_STAT_COUNT, - MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, stats::ionstats.data(), + grid::get_npts_model() * get_includedions() * stats::ION_STAT_COUNT, MPI_DOUBLE, MPI_SUM, + MPI_COMM_WORLD); #endif } } // namespace stats diff --git a/stats.h b/stats.h index fce850f86..3b9497d34 100644 --- a/stats.h +++ b/stats.h @@ -1,45 +1,49 @@ +#pragma once +#include #ifndef STATS_H #define STATS_H +#include + #include "packet.h" namespace stats { // number of ion stats counters that should be divided by the ion populations -#define nstatcounters_ratecoeff 18 +constexpr int nstatcounters_ratecoeff = 18; // one counter per ion per cell enum ionstattypes { ION_RADRECOMB_MACROATOM = 0, ION_RADRECOMB_KPKT = 1, ION_RADRECOMB_ABSORBED = 2, - ION_BOUNDBOUND_MACROATOM = 4, - ION_BOUNDBOUND_ABSORBED = 5, - ION_NTION = 6, - ION_PHOTOION = 7, - ION_PHOTOION_FROMBOUNDFREE = 8, - ION_PHOTOION_FROMBFSAMEELEMENT = 9, - ION_PHOTOION_FROMBFIONPLUSONE = 10, - ION_PHOTOION_FROMBFIONPLUSTWO = 11, - ION_PHOTOION_FROMBFIONPLUSTHREE = 12, - ION_PHOTOION_FROMBFLOWERSUPERLEVEL = 13, - ION_PHOTOION_FROMBOUNDBOUND = 14, - ION_PHOTOION_FROMBOUNDBOUNDIONPLUSONE = 15, - ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTWO = 16, - ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTHREE = 17, - ION_MACROATOM_ENERGYOUT_RADDEEXC = 18, - ION_MACROATOM_ENERGYOUT_RADRECOMB = 19, - ION_MACROATOM_ENERGYOUT_COLLDEEXC = 20, - ION_MACROATOM_ENERGYOUT_COLLRECOMB = 21, - ION_MACROATOM_ENERGYIN_RADEXC = 22, - ION_MACROATOM_ENERGYIN_PHOTOION = 23, - ION_MACROATOM_ENERGYIN_COLLEXC = 24, - ION_MACROATOM_ENERGYIN_COLLION = 25, - ION_MACROATOM_ENERGYIN_NTCOLLION = 27, - ION_MACROATOM_ENERGYIN_TOTAL = 28, - ION_MACROATOM_ENERGYOUT_TOTAL = 29, - ION_MACROATOM_ENERGYIN_INTERNAL = 30, - ION_MACROATOM_ENERGYOUT_INTERNAL = 31, - ION_STAT_COUNT = 32, + ION_BOUNDBOUND_MACROATOM = 3, + ION_BOUNDBOUND_ABSORBED = 4, + ION_NTION = 5, + ION_PHOTOION = 6, + ION_PHOTOION_FROMBOUNDFREE = 7, + ION_PHOTOION_FROMBFSAMEELEMENT = 8, + ION_PHOTOION_FROMBFIONPLUSONE = 9, + ION_PHOTOION_FROMBFIONPLUSTWO = 10, + ION_PHOTOION_FROMBFIONPLUSTHREE = 11, + ION_PHOTOION_FROMBFLOWERSUPERLEVEL = 12, + ION_PHOTOION_FROMBOUNDBOUND = 13, + ION_PHOTOION_FROMBOUNDBOUNDIONPLUSONE = 14, + ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTWO = 15, + ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTHREE = 16, + ION_MACROATOM_ENERGYOUT_RADDEEXC = 17, + ION_MACROATOM_ENERGYOUT_RADRECOMB = 18, + ION_MACROATOM_ENERGYOUT_COLLDEEXC = 19, + ION_MACROATOM_ENERGYOUT_COLLRECOMB = 20, + ION_MACROATOM_ENERGYIN_RADEXC = 21, + ION_MACROATOM_ENERGYIN_PHOTOION = 22, + ION_MACROATOM_ENERGYIN_COLLEXC = 23, + ION_MACROATOM_ENERGYIN_COLLION = 24, + ION_MACROATOM_ENERGYIN_NTCOLLION = 26, + ION_MACROATOM_ENERGYIN_TOTAL = 27, + ION_MACROATOM_ENERGYOUT_TOTAL = 28, + ION_MACROATOM_ENERGYIN_INTERNAL = 29, + ION_MACROATOM_ENERGYOUT_INTERNAL = 30, + ION_STAT_COUNT = 31, }; // global statistics (all cells combined) @@ -70,26 +74,23 @@ enum eventcounters { COUNTER_NT_STAT_TO_EXCITATION = 23, COUNTER_NT_STAT_TO_KPKT = 24, COUNTER_K_STAT_FROM_EARLIERDECAY = 25, - COUNTER_ESCOUNTER = 26, - COUNTER_RESONANCESCATTERINGS = 27, - COUNTER_CELLCROSSINGS = 28, - COUNTER_UPSCATTER = 29, - COUNTER_DOWNSCATTER = 30, - COUNTER_UPDATECELL = 31, - COUNTER_COOLINGRATECALCCOUNTER = 32, - COUNTER_NESC = 33, - COUNTER_COUNT = 34, + COUNTER_INTERACTIONS = 26, + COUNTER_ESCOUNTER = 27, + COUNTER_RESONANCESCATTERINGS = 28, + COUNTER_CELLCROSSINGS = 29, + COUNTER_UPSCATTER = 30, + COUNTER_DOWNSCATTER = 31, + COUNTER_UPDATECELL = 32, + COUNTER_COUNT = 33, }; void init(); -void cleanup(); - void increment_ion_stats(int modelgridindex, int element, int ion, enum ionstattypes ionstattype, double increment); -void increment_ion_stats_contabsorption(const struct packet *pkt_ptr, int modelgridindex, int element, int ion); +void increment_ion_stats_contabsorption(const Packet &pkt, int modelgridindex, int element, int ion); -auto get_ion_stats(int modelgridindex, int element, int ion, enum ionstattypes ionstattype) -> double; +[[nodiscard]] auto get_ion_stats(int modelgridindex, int element, int ion, enum ionstattypes ionstattype) -> double; void set_ion_stats(int modelgridindex, int element, int ion, enum ionstattypes ionstattype, double newvalue); @@ -101,9 +102,9 @@ void increment(enum eventcounters); void pkt_action_counters_reset(); -auto get_counter(enum eventcounters i) -> int; +[[nodiscard]] auto get_counter(enum eventcounters i) -> ptrdiff_t; -void pkt_action_counters_printout(const struct packet *pkt, int nts); +void pkt_action_counters_printout(int nts); void reduce_estimators(); } // namespace stats diff --git a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt index 8dd304787..c7515e6df 100644 --- a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt +++ b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_final.txt @@ -1,25 +1,27 @@ -43bf9f48a0f511482f5fab8a05d204d4 absorption.out -689de69d0631b260607f632cefa3a312 absorptionpol.out +24109f2d09a0dc6f111430bcac62e362 absorption.out +f99d272a5701a67ddbdbe3cc572dfdc0 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 +b9494795649e4c892a5864e1364702ac deposition.out +b235a725a159945147f84384a1295123 emission.out +32a92ebc53f27d5a549d9242cf9ff7a8 emissionpol.out +5c07db44ce0f562f0e7f67202df5ae46 emissiontrue.out +638daf26cdb4249c06fb4475f4f2b078 gamma_light_curve.out +93a975936b353d1f78f275229cdb2a28 gamma_spec.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 20bddb22b6f084a7abc105c210366bfd grid.out -27e9af272dcd0b6a9bd03966e35195c7 light_curve.out +281553bd29e414420511af82b97c2ba2 light_curve.out 5740f40190d45653e5fce57ddf577dc8 linestat.out c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out -b5585182cae11527810cfb82072fb919 packets00_0000.out -3595e9d4484a55eb1d40d1f44fd168fc packets00_0001.out -e3e8093504b5f4ceb4b23924e750f0b2 spec.out -bbf9c836a1bbc64f7919d8c2dccb6b52 specpol.out +d6e3ae254d56e6649c2ea35a81437beb packets00_0000.out +6f6af500b379fdef6c04e2e480d0293a packets00_0001.out +887fe3ebcd66343afa8d283ca8289a0d spec.out +08b4509cde05029747a3015743b0be9b 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 -c17b719d51868938ea2569ee03526ed6 job1/estimators_0000.out -83f41b85cdb98c088c001619135cde42 job1/estimators_0001.out +1ab572d6289a8f7c192ae6e2e9a412e3 vpackets_0000.out +4b2790b819ef0518a4f1f21fc81fd495 vpackets_0001.out +45d2451baa526cfe6789594cfedaf19e vpkt_grid_0-0.out +9261e4760253227d7856d16989573a52 vpkt_grid_1-0.out +96ae0b30d1315ec67794c399f350636f vspecpol_0-0.out +1b1c71cbc0616c3c09527702cea59e58 vspecpol_1-0.out +758593c9177b6eefb1a8e25d7a64752b job1/estimators_0000.out +0c75c9ce7960ee93ed3ec63c2373867d job1/estimators_0001.out \ No newline at end of file diff --git a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt index adff299e0..595eb686d 100644 --- a/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt +++ b/tests/classicmode_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -1,21 +1,23 @@ -fd071bf164378576c70bc25583f0eadb absorption.out +11f8f93d7c842db7db27b2b3265af6ea absorption.out c0604236aa7df99cda43e5c7bbe7b6f4 bflist.out -776e733e780e8fb7509a5b3578bad22a deposition.out -f9368d553c9ce47d2a653fb8d7cd8840 emission.out -d1bfa2500e1516981f0bddf42c8a6038 emissiontrue.out -94f7bdd6bf7c8d2f30e0beb08280805c gamma_light_curve.out +5821d8a1ff0e633054b15e18e2ed98a6 deposition.out +274829c7939376eb1d5824c28b4be191 emission.out +4502ee9ed52ebfe8c4ed554ac0c54904 emissiontrue.out +907d274bcb2fe45a98e61d1c33d5ee3c gamma_light_curve.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 20bddb22b6f084a7abc105c210366bfd grid.out -5624f2243562637cd6d885e4b77e2867 light_curve.out +01ed85b14e70a20006471e4234bd469b light_curve.out 5740f40190d45653e5fce57ddf577dc8 linestat.out c4e4e8d00846618f3931dc01cf52615f modelgridrankassignments.out -1d1e1bba1dc6890d01ec086506017bcf packets00_0000.out -13c47cc21b83945e9f0711328825ff42 packets00_0001.out -814474a12997e69688761d0836d3e98a spec.out +4ad371d778ec13591ade5b3cf859b581 packets00_0000.out +e50e0a0eb82da388d06a8548d893bdd9 packets00_0001.out +8ceb17de8e2794690301d502e0481204 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 -3779b9203782f26c2568c7e9caaa17c1 job0/estimators_0000.out -bee1cab6351770f62701c9fe3f9b7c33 job0/estimators_0001.out +037cf46f584136c4eec9ac53ac0c451b vpackets_0000.out +1a942d94eb24e02f4f256e2aaf37584a vpackets_0001.out +f5a912bb7c2d575104f1be55e38e6656 vpkt_grid_0-0.out +37def760441c053b4f50eb64c762faff vpkt_grid_1-0.out +2553cd0edfc684028256324699c6cbf7 vspecpol_0-0.out +2e22b9de9ca662fb1ad6e61ce8e666a6 vspecpol_1-0.out +411894f38788c399279a3f2ccc2e2e39 job0/estimators_0000.out +264522adaa97be92359332b789aa21e3 job0/estimators_0001.out \ No newline at end of file diff --git a/tests/classicmode_3d_inputfiles/compositiondata.txt b/tests/classicmode_3d_inputfiles/compositiondata.txt new file mode 100644 index 000000000..b0d26d8e3 --- /dev/null +++ b/tests/classicmode_3d_inputfiles/compositiondata.txt @@ -0,0 +1,6 @@ +3 +0 +0 +26 7 1 7 500 0.0 55.8450 +27 7 1 7 500 0.0 58.9332 +28 7 1 7 500 0.0 58.6934 diff --git a/tests/classicmode_3d_inputfiles/results_md5_final.txt b/tests/classicmode_3d_inputfiles/results_md5_final.txt index 28605dfd4..d2a5b4b7b 100644 --- a/tests/classicmode_3d_inputfiles/results_md5_final.txt +++ b/tests/classicmode_3d_inputfiles/results_md5_final.txt @@ -1,524 +1,524 @@ -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 -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 +6aaacd150ac3cb0a274696a43b6de762 absorption.out +2b85ff8d366027b8103ce119172f5904 absorption_res_00.out +3820b971acb8fe7cb45e0baf65ac0bbe absorption_res_01.out +4f21c24008368678c5948395b5c20456 absorption_res_02.out +5efa006b01013a8f2f1e5ec091cb48cc absorption_res_03.out +83f50e4a5f1b043ebc9f237a42bf8d4d absorption_res_04.out +8ac0f301a7ba0d8caa0c90fa5f511794 absorption_res_05.out +c75596d52717e972b7929e1af9664472 absorption_res_06.out +c1f1dfec732ae132e163ee5e886f3de5 absorption_res_07.out +d154657b450468a6b15455a996456643 absorption_res_08.out +0ef7c89a7305b15b2a35b5c6292490c4 absorption_res_09.out +a205fcf5c1ca3550e9f260d8c8c2a8b7 absorption_res_10.out +6d8538f10409822aa39142b553ba3682 absorption_res_11.out +c873785cff45d3184dc34f1c120c1b55 absorption_res_12.out +cbaa46c3b2363693f37c0bdf705d5f27 absorption_res_13.out +21cea727f1246958731ed14ac64588a1 absorption_res_14.out +a634b6be259776622324958021463afc absorption_res_15.out +14b72e21c470958db9f03b5de8f83592 absorption_res_16.out +50fb8a7634f57f45efc7d46fa84b0495 absorption_res_17.out +2254dda0e62960abd68a4658faeccc5a absorption_res_18.out +80a799397a52f577e3f1b7e94e846232 absorption_res_19.out +71491c47ab133463fdb7dc2df5013771 absorption_res_20.out +0427a52c3d1377f8ea92f84cc9881c83 absorption_res_21.out +11d85586bca5a614d52e3cd755e40f29 absorption_res_22.out +6a9cfcafe810c145a0b86358a8567c9d absorption_res_23.out +9ca16584adca04926005a944365b5115 absorption_res_24.out +b60d744e0ec71bb09d06e18b33f36394 absorption_res_25.out +e476d474a46b5c7d05a8da4a7df4a964 absorption_res_26.out +a4beafacdc6df57654975b672d434d93 absorption_res_27.out +f3ed41ff7322cde465d3fb6b3a558fe9 absorption_res_28.out +effdecf5ca3207d4e2e666198912cbd0 absorption_res_29.out +0dd5318f27efe2adde675fb0412fa7f5 absorption_res_30.out +c7eeccb5043a3913353c9edc193f1beb absorption_res_31.out +5d303f3710fa47998869f21d72da1afe absorption_res_32.out +32f9e783c4258f63bc8746f3479b8e3c absorption_res_33.out +ce4db2a89c0b4045eb1cd75617742110 absorption_res_34.out +a01c8ed05525d578eda6b80e50287849 absorption_res_35.out +fba2e4bb166f6f18dca5ffdf3374047e absorption_res_36.out +9047958b3ca11d5f4ad45bbb84a3c08e absorption_res_37.out +99bc408c3c6d749ba978b194912a3543 absorption_res_38.out +9679244d117b4a84e3d118aa0fac4ad3 absorption_res_39.out +0cab227235183c2458d2851fd3e998d0 absorption_res_40.out +c035337b534d5afb40ba90804fa4ee95 absorption_res_41.out +227fab8aa326d86da040cfd76b220102 absorption_res_42.out +701f0c97b8417fb9e73ec48074ed98b5 absorption_res_43.out +bb3d0370d629abd914a9bdd645fb6105 absorption_res_44.out +e9163f746803b66b5d26a4ec09d5cd52 absorption_res_45.out +8dc167cfecada30ca6dd2e96c3ff0483 absorption_res_46.out +da5e87a3afbfb7e607b76a02883997a4 absorption_res_47.out +0919926b79dde466c8a902abde60c249 absorption_res_48.out +a8863c2827ad937eb971f69ec5e9f850 absorption_res_49.out +b729170c79c7ebd65c431527ba37eede absorption_res_50.out +00f50b36ada896e8258302e2c92613b7 absorption_res_51.out +3fb589a9dc1bb5614d5c871c6f4f8fe6 absorption_res_52.out +600bba9851f08418bbf445107a074fc0 absorption_res_53.out +fc5339c81f459c68c36bfff5d372f302 absorption_res_54.out +aceec4d47e13fc8e0a4bf9b95aef5ae4 absorption_res_55.out +d6913e70ec09b6e68964f8c31316ddc7 absorption_res_56.out +2a14cdaf224d24ca472d95429a196bc0 absorption_res_57.out +b5d28de86c7bb2245906fe77064835f3 absorption_res_58.out +c5c80a17f5052675fcffd93c26c8a10f absorption_res_59.out +2eb2c8c3dc579200e71515b3cd531332 absorption_res_60.out +c703dcedd27a3f03c1edaf5bce519fb4 absorption_res_61.out +7b50f7b40aad04a125d7c7bdd56154d5 absorption_res_62.out +70254277225c280ea4e0be185dfea9bd absorption_res_63.out +1344d3c9b5f05d64ea4eb56b522191f0 absorption_res_64.out +42fe60bf2abd3850f69fcf07bb0d013b absorption_res_65.out +09f492a3f4f4413b6fb504ee32897000 absorption_res_66.out +bcfafa2a56094d272e6d8e48538af6ed absorption_res_67.out +e8a5ae973a2dadbb52097ff6b375883f absorption_res_68.out +fba493477f084324480f5543b6e5dec1 absorption_res_69.out +45a1d27c3c519a9984984c2d6ee80bc5 absorption_res_70.out +782c6120385959e16a2ef5c8962f2088 absorption_res_71.out +a115a2f60d1a8953eed1f694217f8082 absorption_res_72.out +cf1e8c138f353efbf9c247e5c7097ae8 absorption_res_73.out +7dacd27fb3f861f218ebdde7809f023b absorption_res_74.out +1d9cc57b83c339b91180801a916229b2 absorption_res_75.out +a492dbc94c231df76f58f01a1f63f7fd absorption_res_76.out +ca670c608e29e0acd2c9ff3e69faac44 absorption_res_77.out +a25a1b5b4336964a116f79d05fed1c00 absorption_res_78.out +fdd2ba1b67a9a7fe74760881459219f2 absorption_res_79.out +b5b160d774a9f83c86e1374753842a83 absorption_res_80.out +a46ee14eb46a9284a2d4630c93cae754 absorption_res_81.out +fcce1088a8851546ab82e8dc8a0f3d71 absorption_res_82.out +7c31092110f3a2d8fd7ee14d9b0037b3 absorption_res_83.out +197863a6885d93ff30285624b2c719b8 absorption_res_84.out +a4c70f778c101b586cb86371a4f0d592 absorption_res_85.out +f698d83918e283b71b7a983be1860955 absorption_res_86.out +cc61ef13ee1c4cc4cd5fac2e85b8e597 absorption_res_87.out +b58db35a65cdc623ceebaa685bc7fbc9 absorption_res_88.out +d1f317389669f2bf005e672d51c0583b absorption_res_89.out +9aea7d09b376a0a056fd5dcfbc162346 absorption_res_90.out +f1477d377e9368b7f1e289a3ce80d58a absorption_res_91.out +9e62d94d9401eb5b807759018481c69f absorption_res_92.out +6c97389b4865cad3fc0c8928e3f85288 absorption_res_93.out +7293ede0934ce9f35c5f050dd634ff2c absorption_res_94.out +0c7a84b41d5f45f7abdb1bbadf6b33a8 absorption_res_95.out +333892f065a9ed292f0c89cde3ed7167 absorption_res_96.out +72d69a33489d773615e8460612798771 absorption_res_97.out +c7503fa84d269ec568da9211a40f41da absorption_res_98.out +45be008592b6df6b3e1b292f0700501d absorption_res_99.out +bd48fcc37d47bdc4e2877e340e11fcaa absorptionpol.out +e9da13d2a940ed3887e5df20531a6839 absorptionpol_res_00.out +33c842693a914704e516c59b48d9b2b4 absorptionpol_res_01.out +85fd5fb30eb3f16b4b1b2f8e64cf5b3d absorptionpol_res_02.out +8865996f5bc899a50edcd78bac347e9c absorptionpol_res_03.out +581439109459366b4ed956361238803f absorptionpol_res_04.out +929aa28050ee4cd248b46c0e6c549231 absorptionpol_res_05.out +dc20991dbba10fafd5524863bc32eec0 absorptionpol_res_06.out +cf180d893b2aef108f7f0c1d382c2c34 absorptionpol_res_07.out +6f9a8715897c8779ae85c9ae6724712a absorptionpol_res_08.out +822aaab95c32d6d07c9cbe1c4cf042a8 absorptionpol_res_09.out +4a84cd7d4740dc1a395e37015bd4a42c absorptionpol_res_10.out +4a736153f15997f70023000804be7c98 absorptionpol_res_11.out +f6dd8a46b42c5faaa5874d22bf97bf7c absorptionpol_res_12.out +306b3cce27dfb156d492ea8920bafbc6 absorptionpol_res_13.out +bd3ec93e5ba0d94fde2542207d939d84 absorptionpol_res_14.out +c18ac73f77af4a16f042ecb57eb18839 absorptionpol_res_15.out +87396b5fdc71c16a351db0a6088b1182 absorptionpol_res_16.out +7990e6f624fa31bcb85a73425652058a absorptionpol_res_17.out +78587ec4111ee522773ce4531f822c5e absorptionpol_res_18.out +2c541aff263e895012e7df3daaed0909 absorptionpol_res_19.out +bcbac1323d6c108ac657c6b17953aa5f absorptionpol_res_20.out +d68cc807a6b0ba55d9744334ac13cf40 absorptionpol_res_21.out +2ff129728f51bf8048607e167433367f absorptionpol_res_22.out +62263ff524a116c2e4bb1868c739be33 absorptionpol_res_23.out +377d85ca8f3cfe4ae39919a7f8f04bd4 absorptionpol_res_24.out +f2c51c0350a0b87fa76aab602160d09f absorptionpol_res_25.out +55cbb2903648c41a9e4efe9671a672d7 absorptionpol_res_26.out +bba1b464e383c4478982710984971552 absorptionpol_res_27.out +22e6b9a5331c8b9871e635e86e278846 absorptionpol_res_28.out +6c35fcc894b4668069be506638ba3d6f absorptionpol_res_29.out +fe328db83fe25100f2b2f1b0c5c351c2 absorptionpol_res_30.out +944c1a591fef50e637c0c1f6f9f4e799 absorptionpol_res_31.out +ee1ef6fa094fdbd21d3dfa91f22fe5e4 absorptionpol_res_32.out +c336d68e7fd1ca669ae32dd21b173476 absorptionpol_res_33.out +3d4f86a17289bb9728867734c89ca0e8 absorptionpol_res_34.out +2ad9ad783fe86320edd899ddd6f744b5 absorptionpol_res_35.out +6af4b9fb504876a97cb38fa18fc6d9ee absorptionpol_res_36.out +79cfe0d143aad7f4faad4fcf39d101d5 absorptionpol_res_37.out +84af8775168e0477759751e5c74f386b absorptionpol_res_38.out +1fad411d621b30eecf74801e8b14107f absorptionpol_res_39.out +bf4003c81b25e4b10708ef64dd5f0822 absorptionpol_res_40.out +37da5cd50eca6f0bf8ec4724930a59a2 absorptionpol_res_41.out +ab493aa5773ead98d5032b2eaeaad19b absorptionpol_res_42.out +f717866b83340fe0b0506d205e31541e absorptionpol_res_43.out +884af100c223d8afa0f655fb8d1501ad absorptionpol_res_44.out +09e05e62bbc8ad283f7ea8be44cfbfb3 absorptionpol_res_45.out +76261f5a0b9e69195a371a22340a41d0 absorptionpol_res_46.out +a9f0f1b06fe923abc2d1115017c116fd absorptionpol_res_47.out +be4f8eb2628cd201fddc141e8334a4e7 absorptionpol_res_48.out +0a39c165eb01cdba1ba03100552048a9 absorptionpol_res_49.out +9791d1e866c4aa90cd765be8473db34c absorptionpol_res_50.out +d9f0baf0c670b8f26f9abdb6b4b83433 absorptionpol_res_51.out +d0e978f0bf120b06c6e50bd911f3e81d absorptionpol_res_52.out +5190b84fcc2f574d18574b7ea3774729 absorptionpol_res_53.out +d703f1a098324acfe8b2b767e735b3ea absorptionpol_res_54.out +a3e6abf4d02976fc410bfe52aa54972e absorptionpol_res_55.out +ea614d98efe8cd52d4a0e36de85119bc absorptionpol_res_56.out +7ad8ad87a4d50d1bb06ddb8c28c1b925 absorptionpol_res_57.out +8677b01b4b26d8086d5be9b7d2c09208 absorptionpol_res_58.out +6ba8bd0986881c0cc5228ade5985e2f1 absorptionpol_res_59.out +a308359740eb12d353f43a2c27fc6a7c absorptionpol_res_60.out +189a309a09c4116ee7780457fd62e37c absorptionpol_res_61.out +13c9667934dbc94d3960d08084a1de5e absorptionpol_res_62.out +0e369890c0714a4d47a7960bddc320cb absorptionpol_res_63.out +ec49ff19eb3539a0887141bb300c49bb absorptionpol_res_64.out +aba6e74ea040c7b8666ec31031317522 absorptionpol_res_65.out +39ca57466cfc26c8111749ff06abfb80 absorptionpol_res_66.out +9d8efa772314fb587b751862b4a054d6 absorptionpol_res_67.out +e85349fed5d7f5b5c41eca79a396d0ce absorptionpol_res_68.out +1779938cb37ab284e65e842bd3386c5c absorptionpol_res_69.out +4872be274a2cf50f8ae0419d74c019d7 absorptionpol_res_70.out +e19f6b62b7642c993affcf1f0ccdd7aa absorptionpol_res_71.out +61ac3be9b066d3519672db55aa30a9c5 absorptionpol_res_72.out +68af2ca95b7eb8ea9e1de534ffd56480 absorptionpol_res_73.out +41d94c66fafe03ccedf9c2abd3fbac1f absorptionpol_res_74.out +ed8a2b01660929b7963016b2e308057e absorptionpol_res_75.out +ed95803514508c9d5683df70406a4962 absorptionpol_res_76.out +e77672757eac6be0f8295fa1395f8824 absorptionpol_res_77.out +e1feab4ec49bac3af44a4e653ebcc38c absorptionpol_res_78.out +edf50b45b23241711fa78efca65fba6a absorptionpol_res_79.out +0d3cd65335a8a8f6bc701a0f77fe20ec absorptionpol_res_80.out +8a87bb46afd23ec0606b935b669929b3 absorptionpol_res_81.out +ba235dda03914cb914384b1189d8ce3b absorptionpol_res_82.out +73e99cb51ae8482a44ef975cd325af8d absorptionpol_res_83.out +0030d26b5e19d13ada1a73b8a22fc66a absorptionpol_res_84.out +79aac3b81e57c25126ab2c366fcc69f1 absorptionpol_res_85.out +cf6717fe289c10a8e97c17439391f9f8 absorptionpol_res_86.out +cd473c111e0bd48a04ff6b887dc65c10 absorptionpol_res_87.out +893f50cb06ffb55a9c6bdcea25494205 absorptionpol_res_88.out +596f37f0ba3b9cb4afe64ef0e9982dee absorptionpol_res_89.out +5d4db4357a34a6d18fd3d220e7043a46 absorptionpol_res_90.out +7a23b63e501ed84dbce9ddbbbfdb0986 absorptionpol_res_91.out +8dbd55b938870069b4fa1be68f58b98a absorptionpol_res_92.out +a8648853f5a7ac9eede3eaa9f1327d5d absorptionpol_res_93.out +a80e56d78832c32600bb6dfbf8af6243 absorptionpol_res_94.out +a9080701b3d4ebeb048ad990b9c0327f absorptionpol_res_95.out +b078253ac6b5776a94461f047ed07ebc absorptionpol_res_96.out +493236c691864e97dbee7ee7597857a7 absorptionpol_res_97.out +93067a7243ebcfb2cebe5fe76bce74d4 absorptionpol_res_98.out +de8a3288368695cbc22e5c3ce214e801 absorptionpol_res_99.out +9cbb3a257dd95b6b3dd92d1d7d913e6c bflist.out +5539a79d1c9ea9b81b0d0bdaf4463205 deposition.out +02b96b733879611160db87cbf7b1ba05 emission.out +a364b75d9d1f9a5d31cc09e0caabe12d emission_res_00.out +05a12ca3c90082e3a850f1d9f8b287c0 emission_res_01.out +382bed6c56944d099bdb46268a493ff3 emission_res_02.out +7014242d21ba06629b466cf941878e81 emission_res_03.out +3756d9d31b7a9033c98a4c11a5b786dc emission_res_04.out +4dabfe9a90028393a14b16b81b2331c2 emission_res_05.out +fd26a60b8a108570af4ca15703e19e17 emission_res_06.out +31a53ed27d1c9bc493aceeb9c64e45b2 emission_res_07.out +dc34923df0c8f683e260bb17b71f605b emission_res_08.out +586f78799bc6fc792ff0b9181316b44e emission_res_09.out +2b3f3ff571de4d5b9d98fe1e0fd28515 emission_res_10.out +b736eac829f743c3bb70de20d30ee91b emission_res_11.out +9677cc74042a11ebf8ebf9b7eccf0fae emission_res_12.out +4c6385e2e714fae6f4c674ce37f50db8 emission_res_13.out +9f7c59f903eb85b321525f730ec6695e emission_res_14.out +82c43a30869acc2715d235c32356de4f emission_res_15.out +72caa3c80f03d07ef11742dc6e554d6c emission_res_16.out +bd3c372010e37b7baa0616a42510ad95 emission_res_17.out +889d31e85b2b7439e7ed4f14a569c1b2 emission_res_18.out +9176a8fb8268cf10918178edc044ac42 emission_res_19.out +8786327f41bd0a73955d6971afabe86b emission_res_20.out +76707efd876ecd682c0c101ed4bff882 emission_res_21.out +228af8f5ffd5510540ad619d6b8b0193 emission_res_22.out +743a0315f1acc5bdf8b2fe29101f6f51 emission_res_23.out +b4d3a55d62f206fa4ffcbd0e37b8b8c0 emission_res_24.out +39d52a9e1e280f3c5564c7e3b2d3f2dc emission_res_25.out +b70f68365a27df3f2544d9ff5b2c4024 emission_res_26.out +4e11826c49ef4c10a79d128072a0099b emission_res_27.out +a79b7430192209828da797d9d9cf61c8 emission_res_28.out +e9e4c8af8fd728fce7e3e3bbbf5b0637 emission_res_29.out +13cfbc76fd3b344455f3e82300bb414d emission_res_30.out +68f7637ee38037a158e33a735a65058f emission_res_31.out +ed3f13a9fed983e50fac66ecbc8f6d29 emission_res_32.out +f7457d820f5207200515ed5bf91ae2de emission_res_33.out +00e8ccc773394407397fc95f6bcd9a3f emission_res_34.out +990cc2970ce8ef0e9a6fe22f46dbabd5 emission_res_35.out +3cca09679f5dfe4b80678bd4a58cc76e emission_res_36.out +a578ee84831c5b5781c3371d9d6c0237 emission_res_37.out +10d72becb5f7ab9756e72f5badee1aa6 emission_res_38.out +d36aaa231188d6b3768940ebdeeaee6e emission_res_39.out +3dfa8a517accaf90eace17e888c9d8db emission_res_40.out +40061ee543cbba9a1f89c4689a6b0428 emission_res_41.out +0c3ef5e4aebb63b7c34985940e6651d3 emission_res_42.out +0675cf847bd161d15930b17afb6e758c emission_res_43.out +10ce9411fa0325bda8bd700855abf18b emission_res_44.out +9c2cbaacfa3961b9641ebe3adfb4f1c1 emission_res_45.out +6e8a96ac76d79281fd163b77aabead82 emission_res_46.out +c42bed2ea72bc9d76d5e6bb21b563bc2 emission_res_47.out +f499d352b422dec294a1967241afb3ec emission_res_48.out +a4314600e354e87a945a6d1fb22217d9 emission_res_49.out +9bc645a7cf0f641fcf7a7fbf018d81cf emission_res_50.out +d96c4df3b4020e4045bce848a140ced5 emission_res_51.out +9ab68fd671676300172650353f82913d emission_res_52.out +7d7f2b1109c2f4672da60a73246a8138 emission_res_53.out +6920affd2186998d62e57ba7931bb3c9 emission_res_54.out +9605224e3302d4343dcf37cedd9e585e emission_res_55.out +78a6caad7811306c187cf4be6dac7c72 emission_res_56.out +75dd70e811d54445ee4e4feb895c8346 emission_res_57.out +74365a4393711d5092bd8849075ddc3c emission_res_58.out +4eb3886beb48e6c077c2a1274b42dd72 emission_res_59.out +19de8c37a87ec6c70dc6df55e036191c emission_res_60.out +c92e48a919e4c676a0b9226c28d57f6a emission_res_61.out +91088422944b27bec03d76679b8ed880 emission_res_62.out +468a0f308112a4e11020ff695893f9e5 emission_res_63.out +8c07db4a8a812eed0cb616b97264ef2d emission_res_64.out +d79cb8321f8c0846063c480a7a2a1967 emission_res_65.out +b39b86440beb31f92bf1a952c0022760 emission_res_66.out +c12c4e4677d2b4c9c041c691d51028d4 emission_res_67.out +8c889838c199dffed76da759b5d7ef49 emission_res_68.out +9c904ff2c2ec893bc41e8988817a0e0b emission_res_69.out +51e3a9a272c22dd09521101ef4a35bb6 emission_res_70.out +f58dee84118e77089c44c3978e158b89 emission_res_71.out +4008b2f0565a20f71d80a436ab85b299 emission_res_72.out +a67df339bdde70ede087ed222aa71e03 emission_res_73.out +8c0125d4eb755ed5680ec192e8f602d6 emission_res_74.out +2e1bb09506548151a5158680f560d825 emission_res_75.out +a5bd6167630321cd9399e266fe7001de emission_res_76.out +fd21cd2f59a52bbec3cca2ff7e2c28a0 emission_res_77.out +1431d6d87a62aaf169783ebe93a26d43 emission_res_78.out +207425495403ce38926ec389b2c4d789 emission_res_79.out +358df6de82b34ec59246de97783ca945 emission_res_80.out +326acb6ed42fe5b511019c60f8954525 emission_res_81.out +cbd204044658ddf45f9097f665227ca1 emission_res_82.out +d1d031e9c272f188dfa8eb3a6ae4b01f emission_res_83.out +a59fdfba3b52f1412ed4770b024f3597 emission_res_84.out +b494e17b7dcbdb717a9a5317c69c1749 emission_res_85.out +7ba6763bb8613b94e2a3f78792ada9c4 emission_res_86.out +5c79323649407d832900ec6303832056 emission_res_87.out +871e3ce4bde08cc27495140c092e4d2b emission_res_88.out +2417d39216b71105e4a3cbb668e4bcdd emission_res_89.out +ff8db5e384e7c6d8cdbb3a8f4ba787a9 emission_res_90.out +7dbe03165d34a2c41fa1b0d124267b8f emission_res_91.out +879e2b8454a259913689c8e7e2133583 emission_res_92.out +7f7c4aa7f6621754d882f56f67804c84 emission_res_93.out +9eef886d71852a87472bead9cbce3f45 emission_res_94.out +5783f5a25bc3a0b902d3deabdbe9aff5 emission_res_95.out +f8f5e1a764ddae17dd5c876f93655e3a emission_res_96.out +7e431dd5f1d20aca28cef728592396f7 emission_res_97.out +f537a890d8307fb062e7ab671b866a5f emission_res_98.out +6031ed33b0430504b89dbf70b6b0e37f emission_res_99.out +239ff1463e61335d0802c4dda675b077 emissionpol.out +200a9b8d57386210b83a30be2d3fdf17 emissionpol_res_00.out +8968807c7dbd7d5069340d75ad224903 emissionpol_res_01.out +c320242d6f7461a018d48599f15dc67a emissionpol_res_02.out +0c2883abba5bddad1c8f18bbb6b32009 emissionpol_res_03.out +0182368c5cd7d18f77b3295ddef5830d emissionpol_res_04.out +14621c67fc21484f1da9720cc1a7b84d emissionpol_res_05.out +4aea9d67e011d89a088448e079a22a76 emissionpol_res_06.out +069ee24a0c56f61ac62d125ba1e83513 emissionpol_res_07.out +de3e1cb391f0794c64851fa2d5e8f8f9 emissionpol_res_08.out +b71dd992f93680a4b1973b69e7aa32ce emissionpol_res_09.out +544af3d7637677365c0c4d325af65935 emissionpol_res_10.out +47442dd95d7e49cf346873bb01007dfb emissionpol_res_11.out +45c21474859524b6a52a9d0bd4317cac emissionpol_res_12.out +8d396c88f7a36cdaad072311d185264c emissionpol_res_13.out +d854fc811f00fde818abef56b8e57ff9 emissionpol_res_14.out +37c83037f48d5ca8930f6be7a554b49c emissionpol_res_15.out +264cdd2a2817f1cf34fa8373e59ebc9b emissionpol_res_16.out +1cbf786debd8940cf73dec71df2579ad emissionpol_res_17.out +3008ffbc40259ded774249d2c31e3524 emissionpol_res_18.out +33f6fed43643468a0bf8036f31563eb0 emissionpol_res_19.out +04a69b47472985732ddfa81e8c9d87e8 emissionpol_res_20.out +e1d8930258125c2358e89ed33c6d6665 emissionpol_res_21.out +b71b9704363bce0bed098cd3a64444c0 emissionpol_res_22.out +eed259b670c774937156fbe0603f82e7 emissionpol_res_23.out +f8fcc34196c144ee8f2f652ad00a6c0d emissionpol_res_24.out +6ab87ab8798dda4c346c701f28041f0e emissionpol_res_25.out +7e76ad900dd81354d152b92e1bd1ee4c emissionpol_res_26.out +a865a5a62e6c8e732ae47ceb6b908f14 emissionpol_res_27.out +1bd26e75eff8c8dec4e37941b6d1daa0 emissionpol_res_28.out +0822374c003692a38bfb2cdf70f7ee0f emissionpol_res_29.out +1270ba15fe9f965aaf05323f707d518f emissionpol_res_30.out +c3fe4518d6344984bec8cabafad39bac emissionpol_res_31.out +6bd432693293e5b64d1ecb10d6589d8d emissionpol_res_32.out +cce80a1809f6e1abaefa8d04544eb60a emissionpol_res_33.out +3318736359cfda2c379e919bfd059bd6 emissionpol_res_34.out +b0fe7d9e7b6efe24da3c60bfb063f5a2 emissionpol_res_35.out +a78ab41e2e39d77190ac76f268c78978 emissionpol_res_36.out +31d8d4ec5f76c48857d04b50a61c7e9c emissionpol_res_37.out +3e31d9df907b0a90630992885f86f7b8 emissionpol_res_38.out +6787c6946126fc3677cdfa2c063961f8 emissionpol_res_39.out +7598997362576532215d01bc56cb839f emissionpol_res_40.out +00b21fe30d115cacd20faf0ca19b3e12 emissionpol_res_41.out +0d497815be7055ff4601e4bc6dd55b12 emissionpol_res_42.out +ccd7ab64283b52b7f7e5233a44a00185 emissionpol_res_43.out +56bc93acde9547d00f65d50d6823d6a0 emissionpol_res_44.out +889277dabbddf8a1c6041a2ea1fda149 emissionpol_res_45.out +933a1586a2083609256a9731f8518e79 emissionpol_res_46.out +30d41fb9aa3fd2ecf5d9b17d07c5d352 emissionpol_res_47.out +ca52e8965533b1aa34889079e6eab352 emissionpol_res_48.out +8474f08a4cdd073e375f666e0e658a1e emissionpol_res_49.out +b66fc4a094ae8ad8aa6273cee71dff16 emissionpol_res_50.out +8aa612b3a85fb938e1e7d3c0071de20a emissionpol_res_51.out +4396c2677a2543967f98d138b2fbd5b8 emissionpol_res_52.out +67a2f5ead83c7611f99b2c802b8c20cf emissionpol_res_53.out +571a7ade0bf5053d844d8c1970d2deec emissionpol_res_54.out +a55e1ddbcef345d8c1438df21e401c97 emissionpol_res_55.out +effd5120067d4d4ae77d08f8da494c6e emissionpol_res_56.out +80023372906995cdbd0f053803edc84a emissionpol_res_57.out +f6ff63b4b743d3da8e6681529605c36d emissionpol_res_58.out +4b92def9bccca760e15376a0e8c874eb emissionpol_res_59.out +80d4af866aafb2973caf3d56bd2732e3 emissionpol_res_60.out +82bc411c85f0c37318e50b2d0edff523 emissionpol_res_61.out +1cdd54a32ca1e01da8d3b6c3295d7d56 emissionpol_res_62.out +5589962e1e1127f2da2e7f16256cb6c9 emissionpol_res_63.out +8c1525cdbf5726e313b0cc0055ddc8e5 emissionpol_res_64.out +62b0aa0c0179dda144702a6167a3f191 emissionpol_res_65.out +e293d44f6ca54ddd3878c85c120db512 emissionpol_res_66.out +8fcdd7f02907267e3e621e93d49aa77f emissionpol_res_67.out +ead7f7d931db110f7fc38fced7503cc7 emissionpol_res_68.out +630e9fe86d5fefc19d60ab14e95b01e0 emissionpol_res_69.out +0944fdeebc6e22550da76670da856338 emissionpol_res_70.out +c46ce0222cc8fae8f98c0474e9100329 emissionpol_res_71.out +9959cccdb0e6fe7622da5bb60bb57d8a emissionpol_res_72.out +341be9f1a549d23e24ed1ebb114638da emissionpol_res_73.out +d08f7db18462a33e6f814767ec65a009 emissionpol_res_74.out +19a79e7be4a6f19656ba0e578ebc7455 emissionpol_res_75.out +433e93548223ec1f89eb08dc475f27f8 emissionpol_res_76.out +1a641b84988c8c368368a03e5d7e8570 emissionpol_res_77.out +76be79fdb9f305e57d1851dda1a0541b emissionpol_res_78.out +26fa07dca289f8569b5ee4f9b9fabc2e emissionpol_res_79.out +5ef0c041cff26414e4388b39ea753ca9 emissionpol_res_80.out +d7d76ba0570c5548bf26f9a1ad51484d emissionpol_res_81.out +eeb21ea29bde1b3e2ba6ae1d02669d7e emissionpol_res_82.out +c43f8ae260eca1e208e6b44d1d486413 emissionpol_res_83.out +026fddbac50c3c17dfddd7b9c8f82454 emissionpol_res_84.out +44dcc8ddb9f69a9c5d0a11404ab74183 emissionpol_res_85.out +9e896e7e76ca6e210b208725afa167af emissionpol_res_86.out +89e6dc526c4f8c282e28012319e3d6d4 emissionpol_res_87.out +c0728c8c2fea7ec0d0dd1095b52c245c emissionpol_res_88.out +89faaa4893eeeb5690b329ae830930b3 emissionpol_res_89.out +8da3aa859daf1de6850e072e826b4dff emissionpol_res_90.out +aec7ad934b867807b6c84db38f94a258 emissionpol_res_91.out +84bdd38cad92d8ea4edcf64b62a4f1ba emissionpol_res_92.out +605dee1cb0d05a7628420c6b3f5efc40 emissionpol_res_93.out +60591579817db7f8f3d09ebcb8200314 emissionpol_res_94.out +c4d4c5f0a1af3ec433e7a5cf04ccbd53 emissionpol_res_95.out +c4e8f2105f7ca848b2a143e474eb6555 emissionpol_res_96.out +2679ac024f4424f35666c1ae76a363dd emissionpol_res_97.out +64d55ae69f2995c804839a435cfa878d emissionpol_res_98.out +eb80b63446d71220fa16d6044177d93c emissionpol_res_99.out +2e5ab04d77fbc104fc325ea79ef5d628 emissiontrue.out +17a8e4ae6397dc91fbdbe26b35a75dd9 emissiontrue_res_00.out +4e3da20d213417155886682592b8d98a emissiontrue_res_01.out +5ddda53c4e4d9a89a954fef24e030f78 emissiontrue_res_02.out +8a77bd3ec7f17aec0436a2f9f1547476 emissiontrue_res_03.out +b56d981b1dba43a43995219c6eca5150 emissiontrue_res_04.out +32c84a765beff954d78eed8986201e0e emissiontrue_res_05.out +4f13226b8351c139ba57ee4c769702f5 emissiontrue_res_06.out +9fb1a0383d26862bd70faf8716c2939a emissiontrue_res_07.out +b3c761846ec7dffe95b84b393b8ceefd emissiontrue_res_08.out +a5f17e4af653e1a3f19f4f21cd847633 emissiontrue_res_09.out +cba821eed818f335807951a8ef4b3a27 emissiontrue_res_10.out +2a6abc0e07fbb21bbe837403a6bba230 emissiontrue_res_11.out +f23e91528a69c9e4c4109ca4ae785a88 emissiontrue_res_12.out +2d3a873e7e42d0d6e3a8c3d6cf5ed322 emissiontrue_res_13.out +13a76debf056f409f8c86dc2daaa8d9e emissiontrue_res_14.out +cc16760a7063f341f15b2c2e04e89b28 emissiontrue_res_15.out +f8a9342955c48ecefc9509a80750728a emissiontrue_res_16.out +98e224cf5a8b14d5afbe3af5b043a6c0 emissiontrue_res_17.out +eb15c1fb856887d41401800232af898b emissiontrue_res_18.out +e5170beded28be118fff66e1f49465a0 emissiontrue_res_19.out +680872822dd13d89fd135c78faf4e478 emissiontrue_res_20.out +9cfef977e6be7865d68171f29152ee37 emissiontrue_res_21.out +c0396c5733ecb67d82dffc1de73dd51b emissiontrue_res_22.out +5390035c0366d87c110a82b89c230fef emissiontrue_res_23.out +88332483b3cf6ab6b4d052aeb175245f emissiontrue_res_24.out +4c2b9c8a86438ba53b0b80ab8f6a3414 emissiontrue_res_25.out +6cbc7a09c38d7e7d596f3443ff32a679 emissiontrue_res_26.out +1c1e99d03dc0df32d45dc7e5a17e0b48 emissiontrue_res_27.out +f0b4a54773fa0660af119c263683ba64 emissiontrue_res_28.out +a79a6a5df27e3f539e64e481801299cc emissiontrue_res_29.out +1ac499b38a0e52ae326e0f2cb8a3ac3f emissiontrue_res_30.out +34c627db718bdd2dcaaf9565e7e695cc emissiontrue_res_31.out +ae08cc1f5fb8d10f7d6137e0c9bd71b0 emissiontrue_res_32.out +7dafa42644c9a12ca59d4eda76b9aa76 emissiontrue_res_33.out +78f2fd584d73ee8df05c47077bfc7a17 emissiontrue_res_34.out +3d96c058260a038cf17041bbe36bc699 emissiontrue_res_35.out +12ec9a1e56a6f209de8cea295ab4037e emissiontrue_res_36.out +e5edeb1be32792f8049e973b12a606d6 emissiontrue_res_37.out +efc9b1f26f8a1a2c6123ff032380649e emissiontrue_res_38.out +c91823d4db6bdaf26e6fb4b1515e8a2d emissiontrue_res_39.out +f0533019e381a1a105b041ee3c93ac84 emissiontrue_res_40.out +0d7bb2aa6d756b0b5d2781525e9bbb24 emissiontrue_res_41.out +169e6dbc7b2dfc5d39ce7b5851e175fa emissiontrue_res_42.out +8b6c1c00619dd108e7e6c3976ff9fd9c emissiontrue_res_43.out +d6635643dce780f81c2c67a3608f4b40 emissiontrue_res_44.out +b0340a1260d892ea8ca266edf429c636 emissiontrue_res_45.out +914f391ecf790e4d63ea162c288258e3 emissiontrue_res_46.out +24fa44f2a27ad4e596de191b4afc0084 emissiontrue_res_47.out +21a8d60c438bd7fa898633657d8d01e6 emissiontrue_res_48.out +b0b558169024dc563d949159aa0385fe emissiontrue_res_49.out +c425686cd3e36d81282a75d8b6f1596f emissiontrue_res_50.out +f699321dbf8d027a45e441cda316cc36 emissiontrue_res_51.out +225c74a95468f17f727638247d6646f0 emissiontrue_res_52.out +36a2386880ec44599c639e8a99fcecf6 emissiontrue_res_53.out +bd844d0889169f9a76c45aca1a6ce13d emissiontrue_res_54.out +c498c436cdf99928d7521c9ca7178f3a emissiontrue_res_55.out +3293106819837c32fbd29290f5dea100 emissiontrue_res_56.out +7bbfdfc4dff910d60bf80e924ed37579 emissiontrue_res_57.out +1890fe222e4ebe880d13abb7442a366b emissiontrue_res_58.out +2ab62939cefcf83a107de7e8b46e5479 emissiontrue_res_59.out +4e001bc462cc4ef7d8d5be2ad3a3c0d9 emissiontrue_res_60.out +82fc866a5f3f5f8cea1368a3b0c660dc emissiontrue_res_61.out +8e37912420ac43b8e3145948b398c9e9 emissiontrue_res_62.out +d1e4a956fa1acab28848871d3bb9fd19 emissiontrue_res_63.out +d91a4f8c303d31bca22a2ead28d2a077 emissiontrue_res_64.out +21184fb3eec6c3ab9327d5e426ff29aa emissiontrue_res_65.out +98b2e6db304c4db5546eb8a29855051a emissiontrue_res_66.out +75958a40bda4ceefe3ddc78e396db720 emissiontrue_res_67.out +35ec9789496b55984e2bde020ed5035b emissiontrue_res_68.out +bbdefc22a34486c8e8eb1230c52280d4 emissiontrue_res_69.out +e606b4388c5e150a1f3c5b98a353b26f emissiontrue_res_70.out +c4d952cd01e4e0d0a783a429fad9b068 emissiontrue_res_71.out +d1657fd77c679acf266344ac9b2080e5 emissiontrue_res_72.out +bedb20a3b12a1d7d8172ceb945e11c8c emissiontrue_res_73.out +53a32cf9e871b680193483de5dd0866f emissiontrue_res_74.out +6499304817f32d94f8ad73fed407352b emissiontrue_res_75.out +b4703b9908115fff6ee1e9141659d1f6 emissiontrue_res_76.out +ed7ba0048db406172a684e079e66d59f emissiontrue_res_77.out +840e8619a7b4c8b957870280158f9c08 emissiontrue_res_78.out +17b40dac8cf152aaec1083205b27b3b6 emissiontrue_res_79.out +6aa35a57318601abe13798f7fdcb3426 emissiontrue_res_80.out +80d6e42c3ae238b63cbabe6f1b1d5753 emissiontrue_res_81.out +55dbfcf7ca2858c3e1d0f35a9b03770a emissiontrue_res_82.out +c1c5bb30e5cb75ef2b2418bb216f9a9c emissiontrue_res_83.out +c00d9dd8f035420ad7d66ef4c1b40cb9 emissiontrue_res_84.out +b072afc7f44a9caae386366ad12bc430 emissiontrue_res_85.out +0d4c721ff73029eeb77d15125eb4364a emissiontrue_res_86.out +b4656b87a80bb4990617229d21cdec9c emissiontrue_res_87.out +60786d3554c2d1f8bb516d4a949efca7 emissiontrue_res_88.out +acbbf35fbbf1ddfe49c4846419106d3a emissiontrue_res_89.out +0487aa7790e07231381884c41bccab57 emissiontrue_res_90.out +693f48cba93140ee3edef68b33a48a16 emissiontrue_res_91.out +eb6264a55ed4d60006745afc212cbcab emissiontrue_res_92.out +84e5ae01b0397ca200966443fab37553 emissiontrue_res_93.out +f2fa77dfd5a304dc5f83d4325c175de4 emissiontrue_res_94.out +92c7202f575b84795adbcf325ef0e09e emissiontrue_res_95.out +4a6605b3f2193959978276632d3c221b emissiontrue_res_96.out +7c79663508e34c2c2e2f316d3ea2697b emissiontrue_res_97.out +15e57d6d637669ccf4254a7410074a54 emissiontrue_res_98.out +5c10438606fd2a665c4140b56c6b0ab3 emissiontrue_res_99.out +3899c21f38dda3f8ab87b2f9695db1fc gamma_light_curve.out +182b31bca2bdebd7a850b311a58e83cd gamma_spec.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out e08bbb965fac36412810b8769a13ab9d grid.out -37ce27fcc45a6837ecc986ad038c112b light_curve.out -0691b11f3a8871fbe0b141ca8e551910 light_curve_res.out -fb8c9b73ab7c5bb1fac556612d74812d linestat.out +0fdd3156b8f72f2b14655652e4b99c70 light_curve.out +49c813f52f78db063419a856a50841ce light_curve_res.out +6a60c8e204b6679e37d10f30386d4948 linestat.out 07e400ff3495dc0b972ebe785db8b3c2 modelgridrankassignments.out -1ce914331cf5e604e7d2a6949d47f2bc packets00_0000.out -898ed91664f4c73c438cf05530c63994 packets00_0001.out -d88b1007563f469de4c3fa6485354950 spec.out -4389f39981b63d4db9c453fa8b8f5ebf spec_res.out -0463bd17f8c10622f2f13c1d4da2fb9f specpol.out -edffea9cc8b28c23286d9438a28f7924 specpol_res.out +2a69e14f0622bc2bfbb00e32633ba85c packets00_0000.out +de230f3db6fb07b4a76e454a5d90f52a packets00_0001.out +b780f82577e64141a4c8402276bd2b33 spec.out +4c574ffb48393d9ba79cb35e67324307 spec_res.out +143ae90c101a11d6647b13ab87e6b655 specpol.out +e4e02e2c84d54217f947c8036d1e0089 specpol_res.out bc01f046eab9bf3802149e0ff63d5069 timesteps.out -2856bee28f5dc1de72dbcd79767f6da1 job1/estimators_0000.out -bfe2e3027aa471cb6775e5d00bc1bd03 job1/estimators_0001.out +4afb4638fa69a7d24a30b367f5843211 job1/estimators_0000.out +8d951a033757f57ede9aac4dd439ef12 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 cb25b6f06..2ac14ccce 100644 --- a/tests/classicmode_3d_inputfiles/results_md5_job0.txt +++ b/tests/classicmode_3d_inputfiles/results_md5_job0.txt @@ -1,17 +1,17 @@ -3d2fdec1e8d7be5d284686950fa8919d absorption.out -6fffc8a4adefb2119fde9ccadbff8151 bflist.out -e77549779647478dae4195e0c7440c75 deposition.out -d267fc8e87ed6de0b6f02134959342db emission.out -9551c22a92df754a2c881a844156256c emissiontrue.out -2371265548b1da29928750800f62f4b7 gamma_light_curve.out +a99504f0f6ca86d9e2aff57f20f7ee9d absorption.out +9cbb3a257dd95b6b3dd92d1d7d913e6c bflist.out +5b31124754b49d826b64acafb7392314 deposition.out +b2aea5bf81aeee2ce78eb7d2849efdd0 emission.out +0687866947a31887dc9a598b34f9f5a4 emissiontrue.out +92542f1f55ece854b0d80c0df8304a6f gamma_light_curve.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out e08bbb965fac36412810b8769a13ab9d grid.out -065249d721a5b797eedb4dd8d564d34f light_curve.out -fb8c9b73ab7c5bb1fac556612d74812d linestat.out +1d8cdb0bee6f55a1eb80d3a18f6a3cef light_curve.out +6a60c8e204b6679e37d10f30386d4948 linestat.out 07e400ff3495dc0b972ebe785db8b3c2 modelgridrankassignments.out -19dd0f6482e8c2a8e5ad4c9c03633f5e packets00_0000.out -663b0393f95a92f6a0f0fd931957f5c9 packets00_0001.out -8aad0eabdebf722460b05185de9a1a8c spec.out +f3120cee010cee2ea0af2bbf4a075754 packets00_0000.out +3b5fabda07493598f6b9467d4de681e5 packets00_0001.out +fb3f4285858a753c24de4d61c709fbcc spec.out bc01f046eab9bf3802149e0ff63d5069 timesteps.out -5015246ae58957425f1276a6796adb08 job0/estimators_0000.out -f755dd3d0ac134553559cfd12fe2f3aa job0/estimators_0001.out +5ccc4503f3b44a5d35dd36c6013e86e9 job0/estimators_0000.out +008fd3b91d628326fc908479cdd771e5 job0/estimators_0001.out diff --git a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt index cd5ceaa23..191cae38e 100644 --- a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt +++ b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_final.txt @@ -1,6 +1,6 @@ e1bbb9e147672e32bb50fba260343584 absorption.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -87aedf284bbaafae235976a623f1c1a5 deposition.out +c075c25a09896046ceb8af9b6f998d83 deposition.out 7f7ee4926056b26206170ae330594d99 emission.out 4b1b8d917ab7f915b6f3b1afcef48011 emissiontrue.out 893ba3deb7c680605311de7f18ff01a2 gamma_light_curve.out @@ -10,8 +10,8 @@ f9bb214eb7f1ac22791a13c8025c4887 bflist.out 9b198e6c609babbe223c63018acb7979 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -33872793c9626adce4163a6f92f225bc packets00_0000.out -0b33390bb2afa6eb72e3eb66f8c6173c packets00_0001.out +bc41266bd7af71f9ad2dbef19fceb931 packets00_0000.out +5640ca76c081103daab5481a948b7437 packets00_0001.out 5fbbcd0c30de4611d0f6e8e3b7644174 spec.out a351f1711fecd60c023d0ba7332092db timesteps.out 2c3b065fff996381b2e0a45c70738f1e job1/estimators_0000.out diff --git a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt index af67dca2f..292f1c51a 100644 --- a/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt +++ b/tests/kilonova_1d_1dgrid_inputfiles/results_md5_job0.txt @@ -1,6 +1,6 @@ 7e47b382ddacd759ae33771c8fba1bc2 absorption.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -6920ce785631e113d3bdec0e8bf42f66 deposition.out +54a15c584936535a52da16c4747ba4be deposition.out 43641ad17d13b4d07954d7fd9a45864a emission.out d23d4ea71b94b1a4fb706f49a4c8c002 emissiontrue.out b82ac74d554da3f567302ccbadff7366 gamma_light_curve.out @@ -9,9 +9,9 @@ b82ac74d554da3f567302ccbadff7366 gamma_light_curve.out 31be3b86c22c343df5b2f0f10c2edd0c light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -d9dcb4378a7ed332f79a5071bae91a9e packets00_0000.out -b0d755d49c026bf4e67bba200191fd35 packets00_0001.out +7c8652d52cd04232598027f9ce28fbaa packets00_0000.out +e763c71053277f450be602d76c2530d6 packets00_0001.out 8269008eb999358088439c0330a996fd spec.out a351f1711fecd60c023d0ba7332092db timesteps.out -13aed5caf0552d2edc24823c650fbb51 job0/estimators_0000.out -7db264f657940fe58fbaeddcd88d9b62 job0/estimators_0001.out +1562cb1d6e35294fef43933942689fee job0/estimators_0000.out +b6fdebf56f3b91161ef78489a3a6df56 job0/estimators_0001.out diff --git a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt index 42b4cff39..3d90de8d2 100644 --- a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt +++ b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_final.txt @@ -1,6 +1,6 @@ a28abc7f3400c76d3b38da6cc3815b13 absorption.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -0ff0e2fc9adf68667ac4bcff8f240d4c deposition.out +47ec1e26673e6d95cf29ad41f51856a1 deposition.out 858da30ef4cfe71f53759dbc7dedd1ff emission.out 56c691bbc3c3e0d93a32e209c64bfc3d emissiontrue.out a24dd7054c3bc0c35be4e74a5f9e6ed3 gamma_light_curve.out @@ -10,8 +10,8 @@ a24dd7054c3bc0c35be4e74a5f9e6ed3 gamma_light_curve.out a180e52d4ee94f2d679a1320ca628b06 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -def7d4e67866038f5a075bc74d1d225b packets00_0000.out -a3bfac14bde7b6e25811144bfeacec4f packets00_0001.out +187dadc1e5ed8bb6cdb25684f2ebb8de packets00_0000.out +cd13ef75f6e694f9062dc47caa96ff32 packets00_0001.out 821414204b3fb0fab174b9895cbc6e68 spec.out a351f1711fecd60c023d0ba7332092db timesteps.out 3c8106ae605fffd169219d901d7ab212 job1/estimators_0000.out diff --git a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt index 3af2dec78..7b2a46fb8 100644 --- a/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt +++ b/tests/kilonova_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -1,6 +1,6 @@ 86efe2dfb75107386eb4a0a2b01825cb absorption.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -fe69c73227acf3be8a84a9c77554eea4 deposition.out +9185d6817583cd68b3cad05eb2368aa6 deposition.out badaf4d5686bac45bcaf19efc6eacb02 emission.out 1cc31f4dc885f52aa180cef4b1dc3dc5 emissiontrue.out 28454eabb842b47ba951914a57add3eb gamma_light_curve.out @@ -9,9 +9,9 @@ badaf4d5686bac45bcaf19efc6eacb02 emission.out 9626cbeee57ff23233a67786179520ae light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out f5b097e63480ea2b54bb88459f72b19a modelgridrankassignments.out -a84d3d4a9c7d1f61394f1b903d5bb244 packets00_0000.out -627178c8fb51ac34030cd547cc9d268d packets00_0001.out +57ed4007507e2d752360602a13d14d5a packets00_0000.out +072bb27a8fd8a4ec54bae4e465641b96 packets00_0001.out 6fe9dc86f38f3b31814186c336019313 spec.out a351f1711fecd60c023d0ba7332092db timesteps.out -103e8461578e4790aefd83f90befebba job0/estimators_0000.out -9bd6654f641a8bd1911caf09f57f9d4c job0/estimators_0001.out +c58117ca6aebfbb76d7635a5493ebec2 job0/estimators_0000.out +edbb07384f94a3c38054aa84e3d36192 job0/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/phixsdata_v2.txt b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/phixsdata_v2.txt new file mode 100644 index 000000000..249e1c191 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/phixsdata_v2.txt @@ -0,0 +1,2 @@ +100 + 3.0000000e-02 diff --git a/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_final.txt b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..1ffd88a1d --- /dev/null +++ b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_final.txt @@ -0,0 +1,320 @@ +b65007ef75f1eaf1a242c7b3119086c7 absorption.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_00.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_01.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_02.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_03.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_04.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_05.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_06.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_07.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_08.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_09.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_10.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_11.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_12.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_13.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_14.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_15.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_16.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_17.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_18.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_19.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_20.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_21.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_22.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_23.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_24.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_25.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_26.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_27.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_28.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_29.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_30.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_31.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_32.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_33.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_34.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_35.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_36.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_37.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_38.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_39.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_40.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_41.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_42.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_43.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_44.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_45.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_46.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_47.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_48.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_49.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_50.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_51.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_52.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_53.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_54.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_55.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_56.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_57.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_58.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_59.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_60.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_61.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_62.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_63.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_64.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_65.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_66.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_67.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_68.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_69.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_70.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_71.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_72.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_73.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_74.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_75.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_76.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_77.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_78.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_79.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_80.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_81.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_82.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_83.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_84.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_85.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_86.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_87.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_88.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_89.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_90.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_91.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_92.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_93.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_94.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_95.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_96.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_97.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_98.out +b65007ef75f1eaf1a242c7b3119086c7 absorption_res_99.out +897316929176464ebc9ad085f31e7284 bflist.out +70922f33824c016edce025de05dd0dc4 deposition.out +0f7222f75cd219f74ce0bfb4e223645f emission.out +b5163411dc920b6a997e5c8c58dfc503 emission_res_00.out +a8edb0c741e0eff6d583abf8608bb2e5 emission_res_01.out +8863be70a7e3ab37681a31c40c60ee28 emission_res_02.out +961d9d0c155dfc8bdf0947e81b5ac6a9 emission_res_03.out +7af565fc116705e17a615d3f4a08eb39 emission_res_04.out +28ff06655788350fae56ef20c0b6a1c1 emission_res_05.out +0158e1b26cb4f69568b333cde51f28e6 emission_res_06.out +4886a3e5651e43ae6b75dd24132b8c7b emission_res_07.out +7675096b63d3e39043c2905219873450 emission_res_08.out +d9e66143551ee3c7e67a11e3f17a58a6 emission_res_09.out +12f7c327460f1d9a0416e440163cf876 emission_res_10.out +2d4c799e631d22f875917849acc1ccff emission_res_11.out +0b7a12c6db4e1fe30aeb57dfb60f920c emission_res_12.out +8e5827eb7dfb65b163a132968caf87db emission_res_13.out +e2f76f1333c0051d3f7b91c58321cc76 emission_res_14.out +492e671683e911c7d89ee61a28ddc325 emission_res_15.out +05b779febf3d18afb85265dddaf24aeb emission_res_16.out +20e15c43f9ce39e4a46dba922689ec2e emission_res_17.out +5f24d557c08b1fce5fe7493eac43a6ab emission_res_18.out +3b04ef8cb0a9d8373e916e389a89c7ba emission_res_19.out +21aaaa35f4d94322cc88832024e56c3d emission_res_20.out +8e4ca75b127da2f9b6c73e575f7bb360 emission_res_21.out +d742e7761f14d27bb67b8684defe2084 emission_res_22.out +6bf02247928e2db682fb81075f9f89bb emission_res_23.out +882dd109ab97beb2e3e6ed073013d0e5 emission_res_24.out +9cb36bfd08df80c91f5c146a168abab5 emission_res_25.out +ad73c5d9cc492d53f9cacdd5e786054c emission_res_26.out +e04fed3bb8127eabf69b7f10c361a2e2 emission_res_27.out +cd9ddf16b768a233224e7fd73e4c02e9 emission_res_28.out +cbf6c671cfe88f5d6d094eb408420931 emission_res_29.out +17e39634cf36e6f5ed0112258b08c458 emission_res_30.out +9081fe5d083e7b41cfdd0bd507556dc6 emission_res_31.out +f8516c0565265f5e186af2dae8d9f952 emission_res_32.out +e27eabc853d48de8df3723bd0cc8c25e emission_res_33.out +0370b1de84d83f659e9cbdaefa5a27ae emission_res_34.out +7a848fb553888360a55bf82680a0d132 emission_res_35.out +571c5677f4a6279b9aa96475cdb2fd62 emission_res_36.out +0f012b8b2d571f5e7ea8ec46394ee2b3 emission_res_37.out +cd606003305fd1bb369ef8249d782c29 emission_res_38.out +34c273de0bfb809368c6265f87a98946 emission_res_39.out +c6fb215d593117f798241fb2c0e6ac88 emission_res_40.out +3e5d3a278b359f62c539c269087520ab emission_res_41.out +7f08421f589ef0796551b3706178f97b emission_res_42.out +08d2ffbe23a9f8e9a79b66bae3ba662b emission_res_43.out +924a0d40c5b1befe899e254ad7e4d7f8 emission_res_44.out +fcc0931f29b74aa8f00c3446b53a32a8 emission_res_45.out +956dfeb2605082fb829c7dfb07db57ef emission_res_46.out +664a60b4cb8785ded7d41c67c27111b6 emission_res_47.out +8e45e6e4dd243dadc2f0a70e0fb6a2e6 emission_res_48.out +cacccbcee17de9dc7d2b62d45d93439d emission_res_49.out +29b475266be137f475e1adb19049180a emission_res_50.out +b2cb2385db15a0cee42d4e021653cf8f emission_res_51.out +2e4529725f27919f69f1d02a97e4c53b emission_res_52.out +baa91fa96914ada288fd840f6e2971c2 emission_res_53.out +7f874b7f463d3a2c8196fb600ffda9e8 emission_res_54.out +12bd2b494174a0b2cedcf7ae3fde106e emission_res_55.out +70692d132c29ce0b9e63a02f7356963a emission_res_56.out +2d35b7af478a7a64c8cb751cf31a7574 emission_res_57.out +2fedc7a60934fa6eba1d06c2d0c88122 emission_res_58.out +5ee1bba0a42dd4c2020c2021e29bbf77 emission_res_59.out +c91c919d4f6fa9f1e8ae8c3251579bc1 emission_res_60.out +8b91ab7d9d47bf878a54ab624c71358c emission_res_61.out +bc378393d5c2c0820c14048fb1f1f15e emission_res_62.out +118159fa8956c57a486bd7d46692688a emission_res_63.out +c82f91440e430fc4e105bc43bfa4b79e emission_res_64.out +5c8126a45abb9dc51dc1e2af933afcd6 emission_res_65.out +f45f9eb80ff3ed65b809ac50b704ff84 emission_res_66.out +e92f64a36512dd1f096fc95dd077a56a emission_res_67.out +f2956eb812d3ef4a8757ab66f626528d emission_res_68.out +f98dda3088ca69a2f2baf29ce1564166 emission_res_69.out +290b8d91f14ea67b77dcf4f2683ed663 emission_res_70.out +6b9eb19075753708999cae92d4b7643c emission_res_71.out +db285dc7d6a75afe11e66c559e4998a8 emission_res_72.out +5b101721cc285f30900ec69f09c90bbc emission_res_73.out +2c1a573f8e26e9d7498216ed40c158c4 emission_res_74.out +3cfcc6945cac8e74158a8e7f33c121b4 emission_res_75.out +8626d3142fac362abd384f281059a129 emission_res_76.out +4c54dfb36d382059afa91206acbba62f emission_res_77.out +f95ae84e7626c8037bde220b1d942433 emission_res_78.out +81a1c1b8ce354ad6a07778bbab6352bf emission_res_79.out +e7371edf0efc15005631d030a8fdc2b6 emission_res_80.out +034f21c3df8f7d28a9ee3e44c280dccd emission_res_81.out +9fbf8041d17d43c51b831f3e102cf143 emission_res_82.out +30b0cfe4b82c929a0a1ff982fb9003e8 emission_res_83.out +7205b46edb4c18f0dfdcb51db13ab4d6 emission_res_84.out +3fe96910817728e3777fd66617167456 emission_res_85.out +20c76818970ab7fca7c038ddf56dfe35 emission_res_86.out +6bf7b6830453d27b8f9176ae57473080 emission_res_87.out +5db523ed3eaef451da8455548585edb5 emission_res_88.out +1d66e265d2ae530fd9e663c7bb42cb7b emission_res_89.out +ee1dd6e7dca8d00cd27fdf7bbc03ce4f emission_res_90.out +7c876e8353563dc8ef354dded049734f emission_res_91.out +7b6c94625070809264dfc0bc7a281e82 emission_res_92.out +01bd6b8ddfe3aca354c310527bca87e9 emission_res_93.out +c0f751da6a2c2a4d0e594f380904f320 emission_res_94.out +f81d7b8b30a149531a5e4949dcd954b1 emission_res_95.out +70534f5bc36fd49b5eb2879da418a731 emission_res_96.out +b9e073a9cc4a5edf66c3b8af47a012bc emission_res_97.out +ecc8b6b2647a972e036af94809941f3a emission_res_98.out +8cee417c8010e0874e1aa4862c07e188 emission_res_99.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_00.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_01.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_02.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_03.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_04.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_05.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_06.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_07.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_08.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_09.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_10.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_11.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_12.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_13.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_14.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_15.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_16.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_17.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_18.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_19.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_20.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_21.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_22.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_23.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_24.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_25.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_26.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_27.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_28.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_29.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_30.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_31.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_32.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_33.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_34.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_35.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_36.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_37.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_38.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_39.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_40.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_41.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_42.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_43.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_44.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_45.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_46.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_47.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_48.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_49.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_50.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_51.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_52.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_53.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_54.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_55.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_56.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_57.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_58.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_59.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_60.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_61.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_62.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_63.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_64.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_65.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_66.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_67.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_68.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_69.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_70.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_71.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_72.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_73.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_74.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_75.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_76.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_77.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_78.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_79.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_80.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_81.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_82.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_83.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_84.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_85.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_86.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_87.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_88.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_89.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_90.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_91.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_92.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_93.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_94.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_95.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_96.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_97.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_98.out +321ece2a7759b37a7da621cd7a4eee90 emissiontrue_res_99.out +19b347d5851c6e71db96861a11ec961f gamma_light_curve.out +3531414318ca94c87e60be5a0553d99a gamma_spec.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +04e99674cf1b694ee76f6d43de93f1fa light_curve.out +fd882a0b2f36bf130c3881312556fd9a light_curve_res.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +ea61d18dab57dab373dee564921ed73b packets00_0000.out +fea213ab616ba4922c4420939561653a packets00_0001.out +9ffcb35dc6d35b5064495646453204ca spec.out +f302d56d0be05fcf91963156684c5341 spec_res.out +a351f1711fecd60c023d0ba7332092db timesteps.out +bb1044bf68bc17cd029a44e1c1224a8c job1/estimators_0000.out +80e5c411cce60395b260e0cfde07aff6 job1/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_job0.txt b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..3366e4332 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_expansionopac_inputfiles/results_md5_job0.txt @@ -0,0 +1,17 @@ +b92a62c73f9262be2a6f6db5294d762d absorption.out +897316929176464ebc9ad085f31e7284 bflist.out +ff5e99151d4c0684ff06f444988a7bea deposition.out +dffe40dafd1de994c3574f64805c1ad7 emission.out +f5902ae8e529c0e852df6b3614fd1597 emissiontrue.out +2b699b60da1c0a791d44c0736efde739 gamma_light_curve.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +79a53fffea2839ec1b12099b56794677 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +d762a090b35415a6340aee8d5ec1ea35 packets00_0000.out +4d8d11edc501629c023fd1a22f8ad131 packets00_0001.out +4c190e954cbedd9ce03c9b6107da8b36 spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +685358667a4c5b9eeb1096470d359834 job0/estimators_0000.out +4e56ed92fdc643c8e281f73084c04a1d job0/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt index ac62fe61b..f3279dd50 100644 --- a/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt +++ b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_final.txt @@ -100,7 +100,7 @@ dce382446e007af2b1677befd0a7f9ea absorption_res_96.out 3f44b670ea956597b7bdf2c88aef05cf absorption_res_98.out bda971ef712f64ae3b72422fe39015a7 absorption_res_99.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -f8ca9ed9a5caf8ae8998c83481372483 deposition.out +a697a5a84a355fdeb9e70242e86643cb deposition.out 4d34b34173e2af0bc6977fd30f3d69a7 emission.out a14f6a7fdc0f7455be5d0c944e94e007 emission_res_00.out 4b6dabd6f73562507c0b402e4ab25d6a emission_res_01.out @@ -311,10 +311,10 @@ e206e98f8f8dfe959caecf570de928d5 emissiontrue_res_95.out 22c46f471ef3c2e126c8d4f37980f117 light_curve_res.out 9773becefdfe4afffb041b703210c67c linestat.out fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out -a1c91f6d89797e1feaf21d4d6186545d packets00_0000.out -2a64c187ff3d5c4cda87068966083983 packets00_0001.out +3acf822b014084fbb44639278da495c0 packets00_0000.out +fa44ad3eba2ecfa90fa251a748d2325e packets00_0001.out 6de6558232711b33cd042ab495c496ca spec.out 0dd442806e68ef09fb6b1d1f6b4a98ff spec_res.out a351f1711fecd60c023d0ba7332092db timesteps.out -df59315c5b70e4891f2144e5eb183e55 job1/estimators_0000.out -395cc0f7be2bd78a2ce13b02c3d3ff4a job1/estimators_0001.out +ad4743f785ee6309cd7762ca502e85fe job1/estimators_0000.out +4ca54ac2bb78aeadc970f118e71303b9 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 index 80d0e04b6..4bca618b9 100644 --- a/tests/kilonova_2d_2dgrid_inputfiles/results_md5_job0.txt +++ b/tests/kilonova_2d_2dgrid_inputfiles/results_md5_job0.txt @@ -1,14 +1,14 @@ f9bb214eb7f1ac22791a13c8025c4887 bflist.out -46149fb7fc6818ce05bbb5b4d6eefb34 deposition.out +6ded687d8cd1179ae31d09b5a36a91f5 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 +b8502c9dbb24231696a5f77761ffb839 packets00_0000.out +39ec03885bdca30cc47c9699247ec60e packets00_0001.out 600fc00772d3c1ad16948b6332791a9a spec.out a351f1711fecd60c023d0ba7332092db timesteps.out -3fcf2b82ba502978e91e69837d81ab4f job0/estimators_0000.out -39a7dbf36a436eaaf3ba4ddc36987b92 job0/estimators_0001.out +9f83f9fc4fb1f37528b7173934209bb1 job0/estimators_0000.out +d8e5283c19a75b9ae164bb791df8d6d8 job0/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/phixsdata_v2.txt b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/phixsdata_v2.txt new file mode 100644 index 000000000..249e1c191 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/phixsdata_v2.txt @@ -0,0 +1,2 @@ +100 + 3.0000000e-02 diff --git a/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_final.txt b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_final.txt new file mode 100644 index 000000000..2f511991b --- /dev/null +++ b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_final.txt @@ -0,0 +1,320 @@ +d5c5a7809099e7896b13c881dd9c2ba7 absorption.out +4eb92f14a50138489405f0611f29e923 absorption_res_00.out +752e0c76919fd9eef3ded7733e04f970 absorption_res_01.out +5002d84687236509224c574f1dac66cf absorption_res_02.out +91b06b4e7b79632415b8fef33e5e9b89 absorption_res_03.out +c24a11940f62388ae6a2b17171f2efc0 absorption_res_04.out +606ccbe2020eb0e8e1ae6725c953c389 absorption_res_05.out +7299841a24d2fd69cf344189b0f25ba3 absorption_res_06.out +4dd07d45ea1dc9f49bbaf8ef948e4d9b absorption_res_07.out +84830dd7bb401979b892abf18d2ce78a absorption_res_08.out +fdd152c5ca12f7e9d2c3a5fc8239d8ee absorption_res_09.out +8e9b19dec8ded72871cedfa373563062 absorption_res_10.out +ca53f20020816a9bbdaaebcfee0e2121 absorption_res_11.out +a28d2a9dd801f02ff344487185708843 absorption_res_12.out +672b0d9801d275209190d9f45dab2d6f absorption_res_13.out +8a305425e389be4731a9732b85c7d020 absorption_res_14.out +c79c228b3bda2a1b878b2114c6e25cbf absorption_res_15.out +60df282d2ad5ea1459c927ce25671db2 absorption_res_16.out +4b47b93792a162a2f51dfb83016eea47 absorption_res_17.out +dc496e5ad63a907c4c89c80bd7bda1e8 absorption_res_18.out +8203f1472eac96c2e60d2d6969c1932e absorption_res_19.out +818f647fc6b67f0774aab70cc7ab4f3d absorption_res_20.out +d5967ef5a1350696d4688e349e48bfd4 absorption_res_21.out +275556023de2f12202ade58b508a4c23 absorption_res_22.out +d291734f50913294cc716b20c1c26456 absorption_res_23.out +2540b2f1682b74f95c7838aebde80c99 absorption_res_24.out +d01790a7b73196017d8f1af67fc83f8e absorption_res_25.out +af8403c4c9392b22aed4f63c93f29e6e absorption_res_26.out +7a5fc74085b06745dff7aca95a01331b absorption_res_27.out +7c3d8ba3fbfc94e401ae4ae1386f6a9f absorption_res_28.out +73722c42add97e1ba072e73caff22deb absorption_res_29.out +c805b93a2aea52d1052d9588ed0e2f69 absorption_res_30.out +b65f54524efe6316f578367d954c1513 absorption_res_31.out +e31dde5a02deada2c26ea2f7f9d3ef18 absorption_res_32.out +3f093ed222ebbf1aef588f228cbe6c0a absorption_res_33.out +8041d1893b6c30be8dff4f001730666e absorption_res_34.out +ead56e6632cadc9741a6c69bcd2bcf08 absorption_res_35.out +0afddd840dd971a25e2767674ff040c3 absorption_res_36.out +7ec45a27e96511efae13beae9dfab77e absorption_res_37.out +df1144008a732f0177693a9c8e077e1d absorption_res_38.out +0dfa737d923119fe63e92b7d863bfbe9 absorption_res_39.out +c1a97b6f308eb614275e4ff0d701ff4c absorption_res_40.out +d01021dd1931bf06bf029182d729f387 absorption_res_41.out +9ade72967418e76e0e786476a61a02f1 absorption_res_42.out +514e32eaa195735069835c6a18165395 absorption_res_43.out +fd31e91e091ebd0a04fca6144034c7d1 absorption_res_44.out +3c840c8ed8a824d335393a6b40dafdd1 absorption_res_45.out +b6c7af1b26c84b15393dfa39aca4154e absorption_res_46.out +8a2a6de2bb65e16654e03becc55f783e absorption_res_47.out +d00b72c4e11e90622baa3093541d6f2b absorption_res_48.out +363bfbd4a971e1c2410025b6f28f3929 absorption_res_49.out +9b82c671a95ba4ab614cc83f1256e64d absorption_res_50.out +88f90cf261df9aa36f1d9c327bdd3177 absorption_res_51.out +a799dead90010af4d0a113adfabe9a10 absorption_res_52.out +ac235c676c5cf0f72bb8797fded58439 absorption_res_53.out +bcc8a53849744bb460101671c2c38442 absorption_res_54.out +202d7ccf89f513f3018b29793bea1b93 absorption_res_55.out +6c90a0e0d0d15363698281914efacf61 absorption_res_56.out +6a069727681096c4cc2ef7e1eedf5463 absorption_res_57.out +ed007a329f3a5f686a186e8ef2ed501b absorption_res_58.out +29fe03a6e6e8748db24bbd556e111512 absorption_res_59.out +21d55b6bebd4aa8adfe5e08572b9a7b9 absorption_res_60.out +145c6beacbc889cdd2640a46500686fc absorption_res_61.out +ce25a689da46a3e5597002f7c15bbda0 absorption_res_62.out +4f06da4c1332e25520f4409fe3540a71 absorption_res_63.out +681be0db15b6426aba38f98498b83c16 absorption_res_64.out +34f7c29ffa00c56c27ad171a6aea4652 absorption_res_65.out +06eb80f0207b82f5e9cd729939edbfeb absorption_res_66.out +650c414f9141ab90dcbdc62ee961f85d absorption_res_67.out +8e9034f1957b2dc1ae895a1fbd541fa4 absorption_res_68.out +b6036040245584c3a7354219daa8975e absorption_res_69.out +e54ecd164c9e0ca16b35458b5ccb235d absorption_res_70.out +e557d2c273baef9c1ee353be64d1e286 absorption_res_71.out +c185dbef030c519a2821d387d3fa26ae absorption_res_72.out +18629d76e406aa562d58234d59686596 absorption_res_73.out +d4632b034480f721a623425e672abbce absorption_res_74.out +b2cf94d3c70a933873ad9e5e9e1e1488 absorption_res_75.out +6d2d0e2bb46aa4f0f55fb3ced2de986e absorption_res_76.out +2b5b2cdb6feeeb61c2e8e2ba60a5d887 absorption_res_77.out +de986a1aee0c8524d7d10b6a578d92da absorption_res_78.out +86b7c2930f2c875d7cd9678cca229fac absorption_res_79.out +af04ee12db318379ca5f551a6decc4dc absorption_res_80.out +94041e154ce27543c06ce8f2432dea26 absorption_res_81.out +ee167e2c0455cfeb740b0511a0c50a75 absorption_res_82.out +e349164bf6a90bb079ebd6d2fad346e1 absorption_res_83.out +4b1ed6e4871233062f5e5cd25afa79fe absorption_res_84.out +805dce31f0e5fd0301d0af164462f7a9 absorption_res_85.out +3b6836074b5bd50d250fafae3df83d5f absorption_res_86.out +1b4dc34ed98a82a326a6b14ff7373de0 absorption_res_87.out +e75fc03f31b0cedc17c31442303df511 absorption_res_88.out +879d485089e0c4e3e2a4232318a9582e absorption_res_89.out +ca5022a36fdb0d7c02ec2a8db5a72913 absorption_res_90.out +256958d871857e7ba11c7ebb07b46f7c absorption_res_91.out +023d8488ffd197f5882a3cecb8b9ebcd absorption_res_92.out +a8f6fa7ae380d262ec7f0c5817d726d9 absorption_res_93.out +0c5d2a771345beb85a10f31a8a705fd7 absorption_res_94.out +2f90ebfd43af28f1ca01a366a8e7ce1a absorption_res_95.out +29252599ca4a692675aaad83a375f43a absorption_res_96.out +73fd02c85e180552545c9cbe370f3b0b absorption_res_97.out +6e767dd5bc5879cb51b28313de28cbd5 absorption_res_98.out +3d4f45803100e4862b8850e5d8db72d7 absorption_res_99.out +897316929176464ebc9ad085f31e7284 bflist.out +e76ec2826e279cac3aefd58283b8c9e0 deposition.out +c13644f7a03479fb91a91537bb80a306 emission.out +e118a7bcef6d636dda1804098b33e408 emission_res_00.out +4e86fe9e03a3925b10b9c252f179966e emission_res_01.out +707353735393e5a71ff0f87537f5ca95 emission_res_02.out +5a7b88dbc8debe32c9664b4103ecf9c5 emission_res_03.out +a0d09e08ffc73b739d1e938dd948da98 emission_res_04.out +d0bf4104d656ad0a95840e402f2f5bce emission_res_05.out +27b7df7c2f26a4e33261209be069bb29 emission_res_06.out +c7ec5989ca44dc7cde626fe45041b089 emission_res_07.out +3b1f4f458235c536685817f01deb289b emission_res_08.out +ebbd6122a9ad5d145e545397a0e488a2 emission_res_09.out +730adfc9ff8afdcb9552a0c6b28efb60 emission_res_10.out +8ec9980a9c91cb21a3322fabd355d89c emission_res_11.out +763741b43a20f4245ac90f0af3976d54 emission_res_12.out +595f48e24fadf52bfc0cb75bfce53dd8 emission_res_13.out +7183fa2646c2d970334f8608bc501c76 emission_res_14.out +e6302d3d5ff17a525c0cbc96eb47bf72 emission_res_15.out +d63a96fe1306146fd89c24876661bab5 emission_res_16.out +ea400110d51ef0bb1b79f02a7c247ddb emission_res_17.out +744a750024162d951cdaa816b13bb1de emission_res_18.out +9524c9aab0f8ee93ab5bd2af17cbebbd emission_res_19.out +78bea915a1d04b99e2dd4fa84ced638b emission_res_20.out +5e0016c5039d75ddf0dae2644db3644c emission_res_21.out +e8074952309a9d93cf92499abf6a2145 emission_res_22.out +fba6fe0005ed2f8968a62dbd5f840081 emission_res_23.out +c42e493e2b00175b8d51c3802c86743a emission_res_24.out +d5a46a88c3b1aefab9519d18f9d9d4bd emission_res_25.out +9b54576938c3ebbfb91dc448fc2eb9df emission_res_26.out +ab3fcf1daae72f8e9032653c3746dcb2 emission_res_27.out +8d4eb63be3e0213f4e25a081ffce00c6 emission_res_28.out +a34faa2e50b84a348a0d75354246d5e0 emission_res_29.out +c688f8c0c812488c40f72193a2866d16 emission_res_30.out +62a8bddb0b97f0848fb0ebb7597300a2 emission_res_31.out +f9cef7ce63f7e24d656742760bd6d9e5 emission_res_32.out +88c72efd4fb17f550422e8fac7dee859 emission_res_33.out +43b60197ed3c5dd2b899acf53215e2ac emission_res_34.out +d7db447f3bcfa981b714a76aade68ac5 emission_res_35.out +68da0a4a29caa347ba7695187e8b1f9e emission_res_36.out +544a3ce0133b0fc2f1bf97b77848c2e7 emission_res_37.out +f2ebc4b17ab6ad5cc44e8e54e2e63f63 emission_res_38.out +e98f54676fa3d1069b2ee61601e98f0f emission_res_39.out +fcf13c8e7292a0cb554418c689d7d3ac emission_res_40.out +010b3ef9c1201f889ca1db8a30f549d9 emission_res_41.out +19375e8e2f196cf6a0def4471dffcae6 emission_res_42.out +ce210b1b84700ad9a23e0eeeedd8ac9a emission_res_43.out +f69ec3c6540254b804ff822408b983bd emission_res_44.out +b9c83d91c48070cb4576e239f4642ea3 emission_res_45.out +546ccd27fb6620f4e03fe6548813d2be emission_res_46.out +2d87bc75a9032d048a1d16c3c2767198 emission_res_47.out +220cf5daab564cfa8812ce2430a8062c emission_res_48.out +efdd7510d847113a1c90d4cb30a4e1c0 emission_res_49.out +9aff591203129b176912c64b4bcfa9a9 emission_res_50.out +33548fe08e3fb59e167a8e7d420405cb emission_res_51.out +c22282718d6a6241320bbee8ce4e5805 emission_res_52.out +5bf2e595a86445c61d293657ba23c1d4 emission_res_53.out +2a2cb6dfbd33bcfd7f38b0810c2d1868 emission_res_54.out +9292833b8d8141d3b3afdb5f23cee015 emission_res_55.out +990fb426b06589163c80cca9c8537799 emission_res_56.out +fb6ed88f3d4a2144ea785c29b9ea08fa emission_res_57.out +a2e5fb76297c7ce3061ab60c5f2bdd16 emission_res_58.out +900a4ac2ce7949caba56697400f4e4e0 emission_res_59.out +f595ca417a9afa0e61f5ebba12d8b811 emission_res_60.out +4c30a4e7fdea41e8bf8beb8c13b56f8d emission_res_61.out +edf2a39a319ad4133cf78d5d0868ed0b emission_res_62.out +04aa9983a0f1db66a277880e8782d4a8 emission_res_63.out +ff50ae92c4449e21d0085a00d6548641 emission_res_64.out +0900445dcadd7b4d4c0b29d8eed0a923 emission_res_65.out +246734093cc5f59ee6e8ff4da0a5c09a emission_res_66.out +0da5e3624be079cd40141e7e6fe53672 emission_res_67.out +0fb57528f9f93fa9a6d8e659d15fe4d4 emission_res_68.out +2554c49cdbe0fd91f00ec8cf3191c089 emission_res_69.out +d6558f932b822503670f29a18b86d387 emission_res_70.out +18c5fd6661e014ef4398d76346177d5d emission_res_71.out +3e8cfc7ed5d3838e1291141c015f96e0 emission_res_72.out +4cd3cd6471cb4b3acb4ea1ff82efa873 emission_res_73.out +0136303ceaabcf74c0fef2816af0aa59 emission_res_74.out +604851e201f316a339ace6206f5d0475 emission_res_75.out +051918dddade06060b4b85090ffce2fb emission_res_76.out +e375bcc086cb5cd043ec4eb90e52e1a1 emission_res_77.out +fca47d8a519054b443302faac1217f13 emission_res_78.out +ba2c93c555bd23f7ac23edc454071b02 emission_res_79.out +a6d81423d2269d4fbd3774c317221b79 emission_res_80.out +1cda369fca86be526ba127e0440719a2 emission_res_81.out +5f080b92492d0817fe88ccf55d453625 emission_res_82.out +0c723106e300aadcf4fe985fbb75cb25 emission_res_83.out +e9519f8e65e9e6545fead921d4ff417a emission_res_84.out +c722dd5830df0be4cb736262d8cdd629 emission_res_85.out +e695e6a56afa8362f936cdf3ae702381 emission_res_86.out +4b1a4a3e0345628797cd568e71b1051b emission_res_87.out +37d365b14c58670abaa8dced9c338ca7 emission_res_88.out +6e1922f7f342fcb5acb998d1a2d32163 emission_res_89.out +170c7b952db881cf763d25defab41cc2 emission_res_90.out +741a5e4c296cf481e6ef5deb640eb5f6 emission_res_91.out +e2e9c221ae1a9123d6a1462cade03bed emission_res_92.out +d814eeca730fd52ccb49970ad2a537ab emission_res_93.out +23a63ad6cdb1c674be69dbef24378c3e emission_res_94.out +fdf67810d98237153d35a3de03e49c8b emission_res_95.out +b03650fa111db6a9bb6ffac435d49a9b emission_res_96.out +e77e1cc3f254e56017c3eedb117e6162 emission_res_97.out +fcce63903a5ee7b07b517dfd58f47f1f emission_res_98.out +4938be5c6bc400583bd9b800ca4dbf96 emission_res_99.out +c8a245ebdd9754886c5369bf175501e2 emissiontrue.out +f598c1fbf83b773173a502759cb8f924 emissiontrue_res_00.out +fcf18376f2c635663cad7e22692aba16 emissiontrue_res_01.out +6ad5f8dfffa52c94dcd89358372d8a59 emissiontrue_res_02.out +41fa6225bd9c89e1e62984907db4ac9e emissiontrue_res_03.out +aab5a3704f82353d799471e485f38723 emissiontrue_res_04.out +2258682d919cbad4bca5bd20a17613e7 emissiontrue_res_05.out +8744c7b1a42eda9aceed04a916542e9e emissiontrue_res_06.out +983b9cb5faa3893005f2e5b22aa011e1 emissiontrue_res_07.out +efc571e17a54bec087cd3aebab52f817 emissiontrue_res_08.out +5c7654603742bd25787426035b213557 emissiontrue_res_09.out +df5a447cb7c1072deac89cc98db781f0 emissiontrue_res_10.out +4b84ecd952d9410d9caae849f457e15c emissiontrue_res_11.out +4489d4e5a64ca2a19cdca7ab25eae2ba emissiontrue_res_12.out +598187400eb2963ac04ac24cf9f185d3 emissiontrue_res_13.out +b503a2135cd92d5e8d33f3ac79b39222 emissiontrue_res_14.out +ed84fa9fd87e4bd4afe5487d670e1feb emissiontrue_res_15.out +e381ac117d7a52519caba03c6f63e8f0 emissiontrue_res_16.out +1f5f9df55cf37e1189e364a90f888987 emissiontrue_res_17.out +760cbec1aa01a0e57fec0a00bdab4482 emissiontrue_res_18.out +a0c14a08fa8f6f815c392e4a4dc05d70 emissiontrue_res_19.out +ac06cb42f0a066f05a016fb3e4526668 emissiontrue_res_20.out +0789a776b18a11b5a3b5a6c174abe083 emissiontrue_res_21.out +9c03c7383ed169610a0d8d6584fc267c emissiontrue_res_22.out +826995e3b9e716ba77669e679adfeffb emissiontrue_res_23.out +e8fb9153b1d25ad0038416dc9a49dd7c emissiontrue_res_24.out +f4f40efcc2a0225fe32aa92baf8fe813 emissiontrue_res_25.out +d8e0a69c5d72f7120404c793cc6770b2 emissiontrue_res_26.out +66fe10974dc7abe2b12fc3a0d5f59d0a emissiontrue_res_27.out +ffbaa6136b7db7161747d49d5dbc8353 emissiontrue_res_28.out +ec8869f1f82de43ee9d16178a4177c8f emissiontrue_res_29.out +862588031c46ceb0ee077b128efd22cf emissiontrue_res_30.out +39f8e0e9811a7dbf3a35a798ae70efb8 emissiontrue_res_31.out +4f3d2f762aac0cce59a9cac113502af5 emissiontrue_res_32.out +f7c5d3c16f31932949a5d0bfbd306113 emissiontrue_res_33.out +164b32344ca6d571bf3c1c8913141556 emissiontrue_res_34.out +1796576afd9e3103a7477550577b99ef emissiontrue_res_35.out +2bc8a8392d22236b74233919eb1333f1 emissiontrue_res_36.out +e896a2acb78fb60f8e907c1f80d3b9f5 emissiontrue_res_37.out +0f3e2dd635e3d643b69aa074dba6c2c3 emissiontrue_res_38.out +99934aad1cbee361672b83147ef5cae5 emissiontrue_res_39.out +576cd26727e83cbadc9aa55d14dbdfef emissiontrue_res_40.out +d2ba771cfc39e5b54a41b58793b0415d emissiontrue_res_41.out +00772d353081df429ddec881f7ea33c2 emissiontrue_res_42.out +9ae0d8d9783548ff1c5aebf9144936d5 emissiontrue_res_43.out +2b3b31882782252bd49ecc513b2e5e0b emissiontrue_res_44.out +4a83a79498c54abf9c3ec78f40a22ad7 emissiontrue_res_45.out +eb22eb79eeca883b8810a697c65dccfa emissiontrue_res_46.out +57f334b55370bb2e8d9d764043422976 emissiontrue_res_47.out +00cfe40311eb9ca2cd2dd4f44bfde8a6 emissiontrue_res_48.out +b8996866afde1314a530ce72c9cf9d59 emissiontrue_res_49.out +19759c777d186edb4318257512d33dd4 emissiontrue_res_50.out +4c6b8a6f9c71b7ff82e335df1670759c emissiontrue_res_51.out +001a561af04be091b171c79ecf4ed8aa emissiontrue_res_52.out +bba4ab4c5f053b1f1853e5471bbead29 emissiontrue_res_53.out +a61b70cc6c55782be4d5ed79cd9720c5 emissiontrue_res_54.out +dd4ba54d1c6436963639b9278e09ac5d emissiontrue_res_55.out +daf98063e4895e690305cc97a2b09f7c emissiontrue_res_56.out +d2ab79321bdf48460a4456b44ac0b9b3 emissiontrue_res_57.out +b180d5ec83e543cfa9599dba45991f98 emissiontrue_res_58.out +eaa0e960a553240545f7b9cd96d2905c emissiontrue_res_59.out +49c3dfacdfa1ae8e384db86600b7f35f emissiontrue_res_60.out +9a44225a0609546a6f7013e4c40f04dd emissiontrue_res_61.out +5d9a127250e58e3b13d13a228c4be9f0 emissiontrue_res_62.out +96aabce803dfaec4a409e5e944581662 emissiontrue_res_63.out +6e7bc0151b96b5309dc2cb52844e20bf emissiontrue_res_64.out +159fa9fc7b4e7fa3125867a5ffcac750 emissiontrue_res_65.out +f0953df80c5afc1b5dc86fbc207b7697 emissiontrue_res_66.out +8385e9710da86dcea3bcdc958163dfdc emissiontrue_res_67.out +ee8d31f541b5ab991c57e42af9dc1397 emissiontrue_res_68.out +5ae20fd49c6311f2c06bc8866493e659 emissiontrue_res_69.out +4d70b57083bc59e40b42b98e3b5ee263 emissiontrue_res_70.out +28fa4471eb13ba128e7e7dbcf6da65bc emissiontrue_res_71.out +36e431c8473970c4de982e683e316218 emissiontrue_res_72.out +fd6ffa18c8873e9b4f1b963617e65cc1 emissiontrue_res_73.out +9826326eb20ddce7583fe3187c190bba emissiontrue_res_74.out +47d3245b7916405f3c2c9aa683e77745 emissiontrue_res_75.out +74c34a47202103c172c1d14555e5daae emissiontrue_res_76.out +184bfe960efbd1372491d5ea425d646a emissiontrue_res_77.out +9dcd708a6c669f6378e0a5502e8b8993 emissiontrue_res_78.out +dc53366d160b57b7db37c050044c6dec emissiontrue_res_79.out +196f75ca8ed6309e3920feaa6cbddfc9 emissiontrue_res_80.out +59a9f7d06c540e8d8dfc8728407446e6 emissiontrue_res_81.out +d88ecf57634ed591d21d1d1b9657b382 emissiontrue_res_82.out +b7d179114554c50d2dc0224bd2613530 emissiontrue_res_83.out +9b6f3d88ce1886138b0e57abebd5374c emissiontrue_res_84.out +5dcb356c79a397506f2872c4b4119e96 emissiontrue_res_85.out +b414f58c646e5198a4110a95032852bf emissiontrue_res_86.out +7502df7fb8d625c12535e7b7540b7f70 emissiontrue_res_87.out +fc1726364585d90f36c120f1c354bd56 emissiontrue_res_88.out +25cb097ad587627dc392a7c75c08a328 emissiontrue_res_89.out +5009a8c2d7682894b8b128900213c93d emissiontrue_res_90.out +02bb6908a353388babded871aeae6c78 emissiontrue_res_91.out +6b126c3dc99c77a92013720e7ae1c6e9 emissiontrue_res_92.out +f8241af6d57f24e58d73bc43bc230788 emissiontrue_res_93.out +b6d9e249b0bb4998477bfcf9e8cf0e4a emissiontrue_res_94.out +e30dcd8e388ae98592fee52240d798d2 emissiontrue_res_95.out +ad1ebe87ec2e7668cf882a4f65437fd0 emissiontrue_res_96.out +30a1b93a8e3dfa665cb2abcff53bedc4 emissiontrue_res_97.out +6333af25d750e6d0c1365eca8ae19f74 emissiontrue_res_98.out +a7ddcb733062b1fbc687ad6560ba0b4d emissiontrue_res_99.out +d0d5e3c2dc3c162321454a8f16d00c9c gamma_light_curve.out +59fb194d6825af8dd80ca984088ec9a9 gamma_spec.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +b4ada0aa594eff7fb0d3c915a57e13aa light_curve.out +5e2ebb769ad18f5e827dc0d7f82fd84e light_curve_res.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +bb1e9438be4c22df2f2f2e3d21f8abd1 packets00_0000.out +fab614a674b04185642991fcf9dde197 packets00_0001.out +72227b0b4343d2abb2af2adab6139d31 spec.out +69a3cd24dfd9426b0bc983f57ebf5f9d spec_res.out +a351f1711fecd60c023d0ba7332092db timesteps.out +8403c6f66256f0176896f3445018041a job1/estimators_0000.out +689dc1ba9e5aca66cd46e6c412467f2a job1/estimators_0001.out diff --git a/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_job0.txt b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_job0.txt new file mode 100644 index 000000000..41471b168 --- /dev/null +++ b/tests/kilonova_2d_2dgrid_xcomgammaphotoion_inputfiles/results_md5_job0.txt @@ -0,0 +1,17 @@ +ee4fc6ed84675b9ca44d0399ec9eda97 absorption.out +897316929176464ebc9ad085f31e7284 bflist.out +e0195d587218b0f92cd43b6044a17b05 deposition.out +22cae210d4305cdb20d9efe260fffcc3 emission.out +a3dac072fd3cb4d72c820e521022adf0 emissiontrue.out +a6d15128205c88676c724fcb12e872b5 gamma_light_curve.out +2b769145664780d4f90b07f963588536 gammalinelist.out +29ac1cb06f3139df7cbca0bbdb426e1c grid.out +48565412fa0ae38578fa8771dc46b404 light_curve.out +9773becefdfe4afffb041b703210c67c linestat.out +fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out +25da3354650ef03d2ce63b2223d2c5f9 packets00_0000.out +99afa86b840ad202c37f8e696f8bab8c packets00_0001.out +ee4b73836c09aef604105a5f0bbbd47d spec.out +a351f1711fecd60c023d0ba7332092db timesteps.out +245c3295403f8227637ba51b03038287 job0/estimators_0000.out +d62cd32e741a7b0d248e5ff405825e92 job0/estimators_0001.out diff --git a/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt index 3a986fb73..951b965ae 100644 --- a/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt +++ b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_final.txt @@ -100,7 +100,7 @@ f9bdd89ecb781b9f6e6ceb5fbf1c0c99 absorption_res_94.out 57e456c0906dda812f6e416f389ccd40 absorption_res_98.out 86b3ba25e36c882fe42f8764ab07b6ce absorption_res_99.out f9bb214eb7f1ac22791a13c8025c4887 bflist.out -8366d3ebcd1ea3ce5e2416970a367ce2 deposition.out +e6e34edad293254e6af4b7d7dc2a0863 deposition.out bc6fa560e008247eadb6689f5c8b9c1e emission.out fce82ffc9c5e49124c218996f5a77eab emission_res_00.out ec34daf448fc654db1481b08a13494c7 emission_res_01.out @@ -311,10 +311,10 @@ b72b67aae10074c2b0915aaad7d9ccbc grid.out d59673879699412cfe041953e4daa1e4 light_curve_res.out 9773becefdfe4afffb041b703210c67c linestat.out fa80b4c76909e5984e9f17d0548a9a73 modelgridrankassignments.out -9e9ea5eb62a7f904d2d52ac2c797e307 packets00_0000.out -14bc4d02c97590b4c351604253126901 packets00_0001.out +967d3df041556c802c72bb66e7d7b0a3 packets00_0000.out +28094c2a199ee6607d6f8ff2988dd2f6 packets00_0001.out f2bea0bcfa94e8edcc00ce440857aa26 spec.out bd96aa33323c64e02bb6b901f304bfbb spec_res.out a351f1711fecd60c023d0ba7332092db timesteps.out -7efbbab22bfa60568079d4b935fed3dd job1/estimators_0000.out -f8a807c360c450e26069965bd41ed4ee job1/estimators_0001.out +70fd776510b5226e2b0868406dad0b13 job1/estimators_0000.out +f3d0f296a9134f10dcf0b9caee69e998 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 index 45724882f..fff18ea08 100644 --- a/tests/kilonova_2d_3dgrid_inputfiles/results_md5_job0.txt +++ b/tests/kilonova_2d_3dgrid_inputfiles/results_md5_job0.txt @@ -1,14 +1,14 @@ f9bb214eb7f1ac22791a13c8025c4887 bflist.out -a02f3e5c0c16eb848950fe775ee4caa8 deposition.out +01a1967b7c2abb14674d80efe9d77076 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 +896c99a2c3e589ac732cdadd949c067e packets00_0000.out +2431b38779b007bc986ff022ed921ad6 packets00_0001.out f8cfd141dfa301309bd0fba60c6f0c71 spec.out a351f1711fecd60c023d0ba7332092db timesteps.out -3f72e6eb4b1e5657f2681eceb7469337 job0/estimators_0000.out -a5a3c6eb38412ad0a274a14f80593191 job0/estimators_0001.out +0970d2071bf460007ecb98035a668703 job0/estimators_0000.out +38b655306a34333a109303f0c4308278 job0/estimators_0001.out diff --git a/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt index 2fa661eb9..5a1549ef1 100644 --- a/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_final.txt @@ -1,20 +1,20 @@ -77a8cefec72da8c0bcebdf4e04d2fcf5 absorption.out +0f80218f5e006e738d41f10911739241 absorption.out aff44ecad68d986f04fcd6110331ba8b bflist.out -9cfd74fab2244cafd4bce62dc6fe2ad7 deposition.out -986056dc9a141a00e7b62e653b2b4f4b emission.out -8607d1185f06e29637171e28955097e1 emissiontrue.out -9aacf70462a00d805820041d07deaa02 gamma_light_curve.out -035228757b6e30f01ff6a04a6a94d364 gamma_spec.out +f4256dada874ae19eadb355a3e5291f8 deposition.out +4b8e160948fa0e4262622fd5421ef0fd emission.out +3e047978b52ef4deef2a774ae9cc6ac0 emissiontrue.out +466d493075dafcb4b60101de6634e0df gamma_light_curve.out +3395346161f4f07a0a80f968b8c7636f gamma_spec.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 47dd2e31cc98d5d1dda67520fb399429 grid.out -2c51d13a376994eb179c6e97d44f1dfd light_curve.out +11e3120f7b6b01185cf4f6f933cb07d5 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out 98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -69cfdb90203ff26d32e2de036f464ca9 packets00_0000.out -d50e7c53ed1d845a36909777fc06f2ca packets00_0001.out -7ff320b87eb952c5e24295c2e12a6258 spec.out +dbcd005ed0d131e8860df06956446ad3 packets00_0000.out +0a97a4118b2161020dbfe2c379e28370 packets00_0001.out +358203b8b6eb08c5e3f492709ad015f7 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 +5b83b49be2c182fee5a2a0b74f9b6ab2 job1/estimators_0000.out +4be02364811482aeb4c793f5ade8f4d2 job1/nlte_0000.out +098fc87f14a17287756a3a4f2cadc922 job1/nonthermalspec_0000.out +6e62ef25ea497f5a9253bffd7d19ebaf 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 index 4650f303d..c83bd6173 100644 --- a/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_job0.txt +++ b/tests/nebularonezone_1d_3dgrid_inputfiles/results_md5_job0.txt @@ -1,19 +1,19 @@ -f5e5d8fc1f4eb7d1d2e7624e6929954c absorption.out +f49774867b219288f87362d6500f97fa absorption.out aff44ecad68d986f04fcd6110331ba8b bflist.out -b1c057449d45d3eafc42a30c2530cb92 deposition.out -91195eb22d4b77ed93bfc5edc96fbd79 emission.out -6bdc6267ae27feab57a34c8a91f78c37 emissiontrue.out -59e10adcf5e9d1c05f935e5583ad9e1e gamma_light_curve.out +c520fc8c095cfcd30e788cbe8368e25e deposition.out +06370cdf659014deb654bef94b1a5fcb emission.out +85385bae7837383447715525a694e7ae emissiontrue.out +bae276e6bf19e86ebdddaf8625809782 gamma_light_curve.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 47dd2e31cc98d5d1dda67520fb399429 grid.out -e0d00b6a32be200c5cf0e9962089c0f3 light_curve.out +0d36d6fe2d76f837e65f497d81fe1d40 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out 98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -b0599d01d7bfc71cc14e2fd2fe1bd313 packets00_0000.out -1b9e11a486b6bd9fb0171566fbe73c17 packets00_0001.out -65539a0900e70d998038852567575617 spec.out +4689ac13b0c15eb06c84cf5cebc7f185 packets00_0000.out +51c5841ac521eda008d39778b4c941ad packets00_0001.out +ee4f0b2582331d708a34c84a142b88f9 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 +3af8502252935c2687816472a08cbe1a job0/estimators_0000.out +5014e7828f79e5a47bc28430a2c07589 job0/nlte_0000.out +098fc87f14a17287756a3a4f2cadc922 job0/nonthermalspec_0000.out +3bfb5acd6d0f18ec923ef5e5faa0c844 job0/radfield_0000.out diff --git a/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_final.txt b/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_final.txt index 9a425378d..05564c81f 100644 --- a/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_final.txt +++ b/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_final.txt @@ -1,20 +1,20 @@ -77a8cefec72da8c0bcebdf4e04d2fcf5 absorption.out +2a7a8e7b14d09d8d9449142d6affab01 absorption.out aff44ecad68d986f04fcd6110331ba8b bflist.out -9cfd74fab2244cafd4bce62dc6fe2ad7 deposition.out -986056dc9a141a00e7b62e653b2b4f4b emission.out -8607d1185f06e29637171e28955097e1 emissiontrue.out -9aacf70462a00d805820041d07deaa02 gamma_light_curve.out -035228757b6e30f01ff6a04a6a94d364 gamma_spec.out +1b66c697ffeb49b8f49ef16e6034e557 deposition.out +8d60ee7aedfe3ae93ea05f7275ef72e1 emission.out +8f8009f6e42b7d296a258a47b59ea62d emissiontrue.out +3b2b080c721f6d34d92511637bc02d90 gamma_light_curve.out +13ed15cfa5ecb2ad81d6f25f71f5e2d7 gamma_spec.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 47dd2e31cc98d5d1dda67520fb399429 grid.out -2c51d13a376994eb179c6e97d44f1dfd light_curve.out +777bef431feedce41df7cfd0a99bdc65 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out 98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -ce06681bb20c3a895bc724caa303cb54 packets00_0000.out -195ea92baa7650d5ae6cab37393a1483 packets00_0001.out -7ff320b87eb952c5e24295c2e12a6258 spec.out +92b849956330137800ae9aa1918e5628 packets00_0000.out +77cbad65da64c6d5bc786a24ff44eda1 packets00_0001.out +84beb7a8468873c0e87ace701f9cd5c0 spec.out 8e7163982f1aa938dc1505478b8c60d1 timesteps.out -463e7d1a82b94f8c0fa7083c1f9b59d7 job1/estimators_0000.out -ebc4df506dc9890bde3a762f581bd5ad job1/nlte_0000.out -1c4b1ccb2ea3bd56f3ef976e4dce870e job1/nonthermalspec_0000.out -ef9c7843dfc4b7dc5e6908270a6bd780 job1/radfield_0000.out \ No newline at end of file +8a549b6db369303ec1e1926078414d3d job1/estimators_0000.out +b3cff112ae236ffd91ab8c02a43b5acd job1/nlte_0000.out +098fc87f14a17287756a3a4f2cadc922 job1/nonthermalspec_0000.out +d6dde8e9cedc7d18aefbecfd267a5014 job1/radfield_0000.out \ No newline at end of file diff --git a/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_job0.txt b/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_job0.txt index fe98f1ce9..8c55ff1f6 100644 --- a/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_job0.txt +++ b/tests/nebularonezone_1d_3dgrid_limitbfest_inputfiles/results_md5_job0.txt @@ -1,19 +1,19 @@ -f5e5d8fc1f4eb7d1d2e7624e6929954c absorption.out +782f6b145460bd745e649e73d179525c absorption.out aff44ecad68d986f04fcd6110331ba8b bflist.out -b1c057449d45d3eafc42a30c2530cb92 deposition.out -91195eb22d4b77ed93bfc5edc96fbd79 emission.out -6bdc6267ae27feab57a34c8a91f78c37 emissiontrue.out -59e10adcf5e9d1c05f935e5583ad9e1e gamma_light_curve.out +e6b1397b0c1d4924c5bf29d7dda5c519 deposition.out +9bef58048b2e2b883eb2f07e47f29612 emission.out +9444209c71cba29ceb4d55ed94c28398 emissiontrue.out +c03cf354af41b3355c5a13d779de5d3e gamma_light_curve.out 057b226c371f3819cba5e04bfea3d114 gammalinelist.out 47dd2e31cc98d5d1dda67520fb399429 grid.out -e0d00b6a32be200c5cf0e9962089c0f3 light_curve.out +1ddae4372b95a1436479520096564578 light_curve.out 9773becefdfe4afffb041b703210c67c linestat.out 98195ccb920b6831d137861c8c6cbfa7 modelgridrankassignments.out -353ee551590fbe95446d10f2554adb9c packets00_0000.out -58ae1da9bf04c21767d40fd722bf737e packets00_0001.out -65539a0900e70d998038852567575617 spec.out +c53862ab86401f76f882e9ce89f274ce packets00_0000.out +c0772a1934e4f61b9b9a4b54ef58addb packets00_0001.out +f253b841ed8ff5d832ce82e6b9266cdd spec.out 8e7163982f1aa938dc1505478b8c60d1 timesteps.out -2a274951a4fff75b7962281cf7bd043c job0/estimators_0000.out -7d86d86b36f9208ba00af17f31846025 job0/nlte_0000.out -1c4b1ccb2ea3bd56f3ef976e4dce870e job0/nonthermalspec_0000.out -fc10e7d1fd915e9372566c536d9d4652 job0/radfield_0000.out \ No newline at end of file +2b54247cf58f0e826f7d497b41c174ec job0/estimators_0000.out +6e93d50a4d1941dbc3f261ca76196a01 job0/nlte_0000.out +098fc87f14a17287756a3a4f2cadc922 job0/nonthermalspec_0000.out +349185aee4a31f3d3cfc4b7180407400 job0/radfield_0000.out \ No newline at end of file diff --git a/tests/setup_classicmode_1d_3dgrid.sh b/tests/setup_classicmode_1d_3dgrid.sh index 2e354f23d..3431d845d 100755 --- a/tests/setup_classicmode_1d_3dgrid.sh +++ b/tests/setup_classicmode_1d_3dgrid.sh @@ -29,6 +29,7 @@ sed -i'' -e 's/constexpr int CUBOID_NCOORDGRID_Z.*/constexpr int CUBOID_NCOORDGR 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 +sed -i'' -e 's/constexpr bool VPKT_WRITE_CONTRIBS.*/constexpr bool VPKT_WRITE_CONTRIBS = true;/g' artisoptions.h cd - diff --git a/tests/setup_classicmode_3d.sh b/tests/setup_classicmode_3d.sh index a5eccd6fc..bbd24b017 100755 --- a/tests/setup_classicmode_3d.sh +++ b/tests/setup_classicmode_3d.sh @@ -6,9 +6,9 @@ runfolder=classicmode_3d_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 +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_feconi.tar.xz --directory $runfolder/ +tar -xf atomicdata_classic.tar.xz --directory $runfolder/ rsync -av classicmode_3d_inputfiles/ $runfolder/ diff --git a/tests/setup_kilonova_2d_2dgrid_expansionopac.sh b/tests/setup_kilonova_2d_2dgrid_expansionopac.sh new file mode 100755 index 000000000..6d0062a09 --- /dev/null +++ b/tests/setup_kilonova_2d_2dgrid_expansionopac.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=kilonova_2d_2dgrid_expansionopac_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_expansionopac_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 + +sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool EXPANSIONOPACITIES_ON.*/constexpr bool EXPANSIONOPACITIES_ON = true;/g' artisoptions.h +sed -i'' -e 's/constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK.*/constexpr bool EXPANSION_OPAC_SAMPLE_KAPPAPLANCK = true;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_kilonova_2d_2dgrid_xcomgammaphotoion.sh b/tests/setup_kilonova_2d_2dgrid_xcomgammaphotoion.sh new file mode 100755 index 000000000..0780ac942 --- /dev/null +++ b/tests/setup_kilonova_2d_2dgrid_xcomgammaphotoion.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env zsh + +set -x + +runfolder=kilonova_2d_2dgrid_xcomgammaphotoion_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_xcomgammaphotoion_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 + +sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h + +sed -i'' -e 's/constexpr bool USE_XCOM_GAMMAPHOTOION.*/constexpr bool USE_XCOM_GAMMAPHOTOION = true;/g' artisoptions.h + +cd - + +set +x diff --git a/tests/setup_nebularonezone_1d_3dgrid.sh b/tests/setup_nebularonezone_1d_3dgrid.sh index 304ca0fea..f21262009 100755 --- a/tests/setup_nebularonezone_1d_3dgrid.sh +++ b/tests/setup_nebularonezone_1d_3dgrid.sh @@ -32,8 +32,6 @@ sed -i'' -e 's/constexpr int FIRST_NLTE_RADFIELD_TIMESTEP.*/constexpr int FIRST_ sed -i'' -e 's/constexpr int DETAILED_BF_ESTIMATORS_USEFROMTIMESTEP.*/constexpr int DETAILED_BF_ESTIMATORS_USEFROMTIMESTEP = 7;/g' artisoptions.h -sed -i'' -e 's/constexpr bool SF_AUGER_CONTRIBUTION_ON.*/constexpr bool SF_AUGER_CONTRIBUTION_ON = false;/g' artisoptions.h - sed -i'' -e 's/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC.*/constexpr bool WRITE_PARTIAL_EMISSIONABSORPTIONSPEC = true;/g' artisoptions.h cd - diff --git a/thermalbalance.cc b/thermalbalance.cc index d733285fe..bb248dea7 100644 --- a/thermalbalance.cc +++ b/thermalbalance.cc @@ -2,12 +2,16 @@ #include #include +#include #include #include +#include #include "artisoptions.h" #include "atomic.h" +#include "constants.h" +#include "globals.h" #include "grid.h" #include "kpkt.h" #include "ltepop.h" @@ -17,10 +21,13 @@ #include "ratecoeff.h" #include "sn3d.h" +namespace { + struct Te_solution_paras { double t_current; int modelgridindex; - struct heatingcoolingrates *heatingcoolingrates; + HeatingCoolingRates *heatingcoolingrates; + const std::vector *bfheatingcoeffs; }; struct gsl_integral_paras_bfheating { @@ -30,37 +37,10 @@ struct gsl_integral_paras_bfheating { float *photoion_xs; }; -auto get_bfheatingcoeff_ana(int element, int ion, int level, int phixstargetindex, double T, double W) -> double { - /// The correction factor for stimulated emission in gammacorr is set to its - /// LTE value. Because the T_e dependence of gammacorr is weak, this correction - /// correction may be evaluated at T_R! - assert_always(USE_LUT_BFHEATING); - double bfheatingcoeff = 0.; - - /*double nnlevel = get_levelpop(cellnumber,element,ion,level); - bfheating = nnlevel * W * interpolate_bfheatingcoeff_below(element,ion,level,T_R);*/ - const int lowerindex = floor(log(T / 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::bfheating_coeff[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; - const double f_lower = globals::bfheating_coeff[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; - - bfheatingcoeff = (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T - T_lower)); - } else { - bfheatingcoeff = globals::bfheating_coeff[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; - } - - return W * bfheatingcoeff; -} - -static auto integrand_bfheatingcoeff_custom_radfield(double nu, void *voidparas) -> double +auto integrand_bfheatingcoeff_custom_radfield(double nu, void *voidparas) -> double /// Integrand to calculate the rate coefficient for bfheating using gsl integrators. { - const struct gsl_integral_paras_bfheating *const params = - static_cast(voidparas); + const auto *const params = static_cast(voidparas); const int modelgridindex = params->modelgridindex; const double nu_edge = params->nu_edge; @@ -76,9 +56,8 @@ static auto integrand_bfheatingcoeff_custom_radfield(double nu, void *voidparas) return sigma_bf * (1 - nu_edge / nu) * radfield::radfield(nu, modelgridindex) * (1 - exp(-HOVERKB * nu / T_R)); } -static auto calculate_bfheatingcoeff(int element, int ion, int level, int phixstargetindex, - int modelgridindex) -> double { - double error = 0.0; +auto calculate_bfheatingcoeff(int element, int ion, int level, int phixstargetindex, int modelgridindex) -> double { + double error = 0.; const double epsrel = 1e-3; const double epsrelwarning = 1e-1; const double epsabs = 0.; @@ -95,7 +74,7 @@ static auto calculate_bfheatingcoeff(int element, int ion, int level, int phixst // const double sf_Te = calculate_sahafact(element,ion,level,upperionlevel,T_e,E_threshold); // const double sf_TR = calculate_sahafact(element,ion,level,upperionlevel,T_R,E_threshold); - struct gsl_integral_paras_bfheating intparas = { + const gsl_integral_paras_bfheating intparas = { .nu_edge = nu_threshold, .modelgridindex = modelgridindex, .T_R = grid::get_TR(modelgridindex), @@ -103,19 +82,12 @@ static auto calculate_bfheatingcoeff(int element, int ion, int level, int phixst // intparas.Te_TR_factor = sqrt(T_e/T_R) * sf_Te / sf_TR; - double bfheating = 0.0; - const gsl_function F_bfheating = {.function = &integrand_bfheatingcoeff_custom_radfield, .params = &intparas}; + double bfheating = 0.; gsl_error_handler_t *previous_handler = gsl_set_error_handler(gsl_error_handler_printout); - const int status = gsl_integration_qag(&F_bfheating, nu_threshold, nu_max_phixs, epsabs, epsrel, GSLWSIZE, - GSL_INTEG_GAUSS61, gslworkspace, &bfheating, &error); - // const int status = gsl_integration_qags( - // &F_bfheating, nu_threshold, nu_max_phixs, epsabs, epsrel, - // GSLWSIZE, workspace_bfheating, &bfheating, &error); - // const int status = radfield_integrate( - // &F_bfheating, nu_threshold, nu_max_phixs, epsabs, epsrel, - // GSLWSIZE, GSL_INTEG_GAUSS61, workspace_bfheating, &bfheating, &error); + const int status = integrator( + intparas, nu_threshold, nu_max_phixs, epsabs, epsrel, GSL_INTEG_GAUSS61, &bfheating, &error); if (status != 0 && (status != 18 || (error / bfheating) > epsrelwarning)) { printout( @@ -131,14 +103,37 @@ static auto calculate_bfheatingcoeff(int element, int ion, int level, int phixst return bfheating; } -static auto get_bfheatingcoeff(int element, int ion, int level) -> double -// depends only the radiation field -// no dependence on T_e or populations -{ - return globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].bfheatingcoeff; +} // anonymous namespace + +auto get_bfheatingcoeff_ana(int element, int ion, int level, int phixstargetindex, double T_R, double W) -> double { + /// The correction factor for stimulated emission in gammacorr is set to its + /// LTE value. Because the T_e dependence of gammacorr is weak, this correction + /// correction may be evaluated at T_R! + assert_always(USE_LUT_BFHEATING); + double bfheatingcoeff = 0.; + + const int lowerindex = floor(log(T_R / 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::bfheating_coeff[get_bflutindex(upperindex, element, ion, level, phixstargetindex)]; + const double f_lower = globals::bfheating_coeff[get_bflutindex(lowerindex, element, ion, level, phixstargetindex)]; + + bfheatingcoeff = (f_lower + (f_upper - f_lower) / (T_upper - T_lower) * (T_R - T_lower)); + } else { + bfheatingcoeff = globals::bfheating_coeff[get_bflutindex(TABLESIZE - 1, element, ion, level, phixstargetindex)]; + } + + return W * bfheatingcoeff; } -void calculate_bfheatingcoeffs(int modelgridindex) { +void calculate_bfheatingcoeffs(int modelgridindex, std::vector &bfheatingcoeffs) { + // depends only the radiation field + // no dependence on T_e or populations + + bfheatingcoeffs.resize(get_includedlevels()); const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); const double minelfrac = 0.01; for (int element = 0; element < get_nelements(); element++) { @@ -152,8 +147,8 @@ void calculate_bfheatingcoeffs(int modelgridindex) { for (int level = 0; level < nlevels; level++) { double bfheatingcoeff = 0.; if (grid::get_elem_abundance(modelgridindex, element) > minelfrac || USE_LUT_BFHEATING) { - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); - phixstargetindex++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { if constexpr (!USE_LUT_BFHEATING) { bfheatingcoeff += calculate_bfheatingcoeff(element, ion, level, phixstargetindex, modelgridindex); } else { @@ -171,16 +166,15 @@ void calculate_bfheatingcoeffs(int modelgridindex) { const int index_in_groundlevelcontestimator = globals::elements[element].ions[ion].levels[level].closestgroundlevelcont; if (index_in_groundlevelcontestimator >= 0) { - bfheatingcoeff *= - globals::bfheatingestimator[nonemptymgi * get_includedions() + index_in_groundlevelcontestimator]; + bfheatingcoeff *= globals::bfheatingestimator[nonemptymgi * globals::nbfcontinua_ground + + index_in_groundlevelcontestimator]; } } } - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].bfheatingcoeff = bfheatingcoeff; + bfheatingcoeffs[get_uniquelevelindex(element, ion, level)] = bfheatingcoeff; } } } - globals::cellhistory[tid].bfheating_mgi = modelgridindex; } static auto get_heating_ion_coll_deexc(const int modelgridindex, const int element, const int ion, const double T_e, @@ -211,7 +205,8 @@ static auto get_heating_ion_coll_deexc(const int modelgridindex, const int eleme } static void calculate_heating_rates(const int modelgridindex, const double T_e, const double nne, - struct heatingcoolingrates *heatingcoolingrates) + HeatingCoolingRates *heatingcoolingrates, + const std::vector &bfheatingcoeffs) /// Calculate the heating rates for a given cell. Results are returned /// via the elements of the heatingrates data structure. { @@ -221,8 +216,6 @@ static void calculate_heating_rates(const int modelgridindex, const double T_e, double bfheating = 0.; double ffheating = 0.; - assert_always(globals::cellhistory[tid].bfheating_mgi == modelgridindex); - for (int element = 0; element < get_nelements(); element++) { const int nions = get_nions(element); if constexpr (DIRECT_COL_HEAT) { @@ -230,57 +223,9 @@ static void calculate_heating_rates(const int modelgridindex, const double T_e, C_deexc += get_heating_ion_coll_deexc(modelgridindex, element, ion, T_e, nne); } } - // - // /// Collisional heating: recombination to lower ionization stage - // /// ------------------------------------------------------------ - // /// For the moment we deal only with ionisations to the next ions groundlevel. - // /// For speed issues (reduced number of calls to epsilon) this is checked here - // /// instead of the more general way to check in col_recomb! - // if (ion > 0 && level == 0) /// Check whether lower ionisation stage available - // { - // for (lower = 0; lower < nlevels_lowerion; lower++) - // { - // epsilon_trans = epsilon_current - epsilon(element,ion-1,lower); - // C = col_recombination(pkt_ptr,lower,epsilon_trans)*epsilon_trans; - // C_recomb += C; - // } - // } - // - // - // /* - // /// Bound-free heating (analytical calculation) - // /// ------------------------------------------- - // /// We allow bound free-transitions only if there is a higher ionisation stage - // /// left in the model atom to match the bound-free absorption in the rpkt routine. - // /// There this condition is needed as we can only ionise to existing ionisation - // /// stage even if there would be further ionisation stages in nature which - // /// are not included in the model atom. - // if (ion < nions-1) - // { - // epsilon_trans = epsilon(element,ion+1,0) - epsilon_current; - // /// NB: W comes from the fact, that the W coming from the radiation field was factored - // /// out in the precalculation of the bf-heating coefficient (this is justified by the - // /// linear dependence on W). - // /// The rate coefficient is calculated under the assumption T_e=T_R because its direct - // /// T_e dependence is very weak. This means we have to pass T_R as the temperature - // /// even if we are iterating here on T_e. (Otherwise we would allow a large temperature - // /// range for T_R which changes the coefficient strongly). - // //C = interpolate_bfheatingcoeff(element,ion,level,T_R)*W*nnlevel; - // C = nnlevel * (W*interpolate_bfheatingcoeff_below(element,ion,level,T_R));// + - // W_D*interpolate_bfheatingcoeff_above(element,ion,level,T_D)); - // - // /// Exact calculation of the bf-heating coefficients using integrators. - // /// This makes things SLOW!!! - // //bfheatingparas.nu_edge = epsilon_trans/H; - // //F_bfheating.params = &bfheatingparas; - // /// Discuss about the upper frequency limit (here 1e16 Hz) which we should choose - // //gsl_integration_qag(&F_bfheating, bfheatingparas.nu_edge, 10*bfheatingparas.nu_edge, 0, intaccuracy, - // 1024, 6, wspace, &bfhelper, &error); - // //C = bfhelper * FOURPI * nnlevel; - // bfheating += C; - // } - // */ - // } + + /// Collisional heating: recombination to lower ionization stage (not included) + /// ------------------------------------------------------------ /// Bound-free heating (renormalised analytical calculation) /// -------------------------------------------------------- @@ -294,40 +239,21 @@ static void calculate_heating_rates(const int modelgridindex, const double T_e, const int nbflevels = get_ionisinglevels(element, ion); for (int level = 0; level < nbflevels; level++) { const double nnlevel = get_levelpop(modelgridindex, element, ion, level); - bfheating += nnlevel * get_bfheatingcoeff(element, ion, level); + bfheating += nnlevel * bfheatingcoeffs[get_uniquelevelindex(element, ion, level)]; } } } - /// Free-free heating - /// ----------------- - /// From estimators - ffheating = globals::ffheatingestimator[modelgridindex]; - - /// Analytical calculation using T, and populations - /// This is always taken as an additional process to the other importantheatingterms - /// because its not done per ion but overall ions. - /* - gsl_function F_ffheating; - F_ffheating.function = &ffheating_integrand_gsl; - - gslintegration_ffheatingparas intparas; - intparas.T_e = T_e; - intparas.cellnumber = cellnumber; - F_ffheating.params = &intparas; - - /// Discuss about the upper frequency limit (here 1e16 Hz) which we should choose - gsl_integration_qag(&F_ffheating, 0, 1e16, 0, intaccuracy, 1024, GSL_INTEG_GAUSS61, wspace, &ffheating, &error); - /// or this integrator - //gsl_integration_qng(&F_ffheating, 0, 1e16, 0, intaccuracy, &ffheating, &error, &neval); - ffheating *= FOURPI; - */ + /// Free-free heating (from estimators) + + const int nonemptymgi = grid::get_modelcell_nonemptymgi(modelgridindex); + ffheating = globals::ffheatingestimator[nonemptymgi]; if constexpr (DIRECT_COL_HEAT) { heatingcoolingrates->heating_collisional = C_deexc; } else { /// Collisional heating (from estimators) - heatingcoolingrates->heating_collisional = globals::colheatingestimator[modelgridindex]; // C_deexc + C_recomb; + heatingcoolingrates->heating_collisional = globals::colheatingestimator[nonemptymgi]; // C_deexc + C_recomb; } heatingcoolingrates->heating_bf = bfheating; @@ -337,11 +263,11 @@ static void calculate_heating_rates(const int modelgridindex, const double T_e, static auto T_e_eqn_heating_minus_cooling(const double T_e, void *paras) -> double /// Thermal balance equation on which we have to iterate to get T_e { - const struct Te_solution_paras *const params = static_cast(paras); + const Te_solution_paras *const params = static_cast(paras); const int modelgridindex = params->modelgridindex; const double t_current = params->t_current; - struct heatingcoolingrates *heatingcoolingrates = params->heatingcoolingrates; + auto *heatingcoolingrates = params->heatingcoolingrates; /// Set new T_e guess for the current cell and update populations // globals::cell[cellnumber].T_e = T_e; @@ -351,7 +277,7 @@ static auto T_e_eqn_heating_minus_cooling(const double T_e, void *paras) -> doub /// Then calculate heating and cooling rates kpkt::calculate_cooling_rates(modelgridindex, heatingcoolingrates); - calculate_heating_rates(modelgridindex, T_e, nne, heatingcoolingrates); + calculate_heating_rates(modelgridindex, T_e, nne, heatingcoolingrates, *params->bfheatingcoeffs); heatingcoolingrates->nt_frac_heating = nonthermal::get_nt_frac_heating(modelgridindex); heatingcoolingrates->heating_dep = @@ -375,19 +301,21 @@ static auto T_e_eqn_heating_minus_cooling(const double T_e, void *paras) -> doub } void call_T_e_finder(const int modelgridindex, const int timestep, const double t_current, const double T_min, - const double T_max, struct heatingcoolingrates *heatingcoolingrates) { + const double T_max, HeatingCoolingRates *heatingcoolingrates, + const std::vector &bfheatingcoeffs) { const double T_e_old = grid::get_Te(modelgridindex); printout("Finding T_e in cell %d at timestep %d...", modelgridindex, timestep); - struct Te_solution_paras paras = { - .t_current = t_current, .modelgridindex = modelgridindex, .heatingcoolingrates = heatingcoolingrates}; + Te_solution_paras paras = {.t_current = t_current, + .modelgridindex = modelgridindex, + .heatingcoolingrates = heatingcoolingrates, + .bfheatingcoeffs = &bfheatingcoeffs}; gsl_function find_T_e_f = {.function = &T_e_eqn_heating_minus_cooling, .params = ¶s}; double thermalmin = T_e_eqn_heating_minus_cooling(T_min, find_T_e_f.params); double thermalmax = T_e_eqn_heating_minus_cooling(T_max, find_T_e_f.params); - // printout("(heating - cooling) at T_min: %g, at T_max: %g\n", thermalmin, thermalmax); if (!std::isfinite(thermalmin) || !std::isfinite(thermalmax)) { printout( "[abort request] call_T_e_finder: non-finite results in modelcell %d (T_R=%g, W=%g). T_e forced to be " @@ -396,7 +324,7 @@ void call_T_e_finder(const int modelgridindex, const int timestep, const double thermalmax = thermalmin = -1; } - double T_e = NAN; + double T_e{NAN}; /// Check whether the thermal balance equation has a root in [T_min,T_max] if (thermalmin * thermalmax < 0) { /// If it has, then solve for the root T_e diff --git a/thermalbalance.h b/thermalbalance.h index c8dbd95b2..6150d5e96 100644 --- a/thermalbalance.h +++ b/thermalbalance.h @@ -1,7 +1,10 @@ +#pragma once #ifndef THERMALBALANCE_H #define THERMALBALANCE_H -struct heatingcoolingrates { +#include + +struct HeatingCoolingRates { double cooling_collisional; double cooling_fb; double cooling_ff; @@ -14,8 +17,9 @@ struct heatingcoolingrates { }; void call_T_e_finder(int modelgridindex, int timestep, double t_current, double T_min, double T_max, - struct heatingcoolingrates *heatingcoolingrates); -auto get_bfheatingcoeff_ana(int element, int ion, int level, int phixstargetindex, double T, double W) -> double; -void calculate_bfheatingcoeffs(int modelgridindex); + HeatingCoolingRates *heatingcoolingrates, const std::vector &bfheatingcoeffs); +[[nodiscard]] auto get_bfheatingcoeff_ana(int element, int ion, int level, int phixstargetindex, double T_R, + double W) -> double; +void calculate_bfheatingcoeffs(int modelgridindex, std::vector &bfheatingcoeffs); #endif // THERMALBALANCE_H diff --git a/update_grid.cc b/update_grid.cc index 945107d84..d7870d8a0 100644 --- a/update_grid.cc +++ b/update_grid.cc @@ -1,6 +1,15 @@ #include "update_grid.h" +#ifdef MPI_ON +#include +#endif + +#include #include +#include +#include +#include +#include #include "artisoptions.h" #include "atomic.h" @@ -10,7 +19,6 @@ #include "grid.h" #include "kpkt.h" #include "ltepop.h" -#include "macroatom.h" #include "nltepop.h" #include "nonthermal.h" #include "radfield.h" @@ -21,836 +29,728 @@ #include "thermalbalance.h" #include "vpkt.h" -static void write_to_estimators_file(FILE *estimators_file, const int mgi, const int timestep, const int titer, - const struct heatingcoolingrates *heatingcoolingrates) { +namespace { + +void write_to_estimators_file(FILE *estimators_file, const int mgi, const int timestep, const int titer, + const HeatingCoolingRates *heatingcoolingrates) { // return; disable for better performance (if estimators files are not needed) - const time_t sys_time_start_write_estimators = time(nullptr); - - if (grid::get_numassociatedcells(mgi) > 0) { - printout("writing to estimators file timestep %d cell %d...\n", timestep, mgi); - - const auto T_e = grid::get_Te(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); - fprintf(estimators_file, - "timestep %d modelgridindex %d titeration %d TR %g Te %g W %g TJ %g grey_depth %g thick %d nne %g Ye %g " - "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::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 (globals::total_nlte_levels > 0) { - nltepop_write_to_file(mgi, timestep); + if (grid::get_numassociatedcells(mgi) < 1) { + // modelgrid cells that are not represented in the simulation grid + fprintf(estimators_file, "timestep %d modelgridindex %d EMPTYCELL\n\n", timestep, mgi); + fflush(estimators_file); + return; + } + + const auto nonemptymgi = grid::get_modelcell_nonemptymgi(mgi); + + const auto sys_time_start_write_estimators = std::time(nullptr); + + printout("writing to estimators file timestep %d cell %d...\n", timestep, mgi); + + const auto T_e = grid::get_Te(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); + fprintf(estimators_file, + "timestep %d modelgridindex %d titeration %d TR %g Te %g W %g TJ %g grey_depth %g thick %d nne %g Ye %g " + "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::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 (globals::total_nlte_levels > 0) { + nltepop_write_to_file(mgi, timestep); + } + + for (int element = 0; element < get_nelements(); element++) { + if (grid::get_elem_abundance(mgi, element) <= 0.) { // skip elements with no abundance + continue; } - for (int element = 0; element < get_nelements(); element++) { - if (grid::get_elem_abundance(mgi, element) <= 0.) { // skip elements with no abundance - continue; + fprintf(estimators_file, "populations Z=%2d", get_atomicnumber(element)); + const int nions = get_nions(element); + if (nions > 0) { + // add spaces for missing lowest ion stages to match other elements + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); } + } + double elpop = 0.; + for (int ion = 0; ion < nions; 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); - fprintf(estimators_file, "populations Z=%2d", get_atomicnumber(element)); - const int nions = get_nions(element); - if (nions > 0) { - // add spaces for missing lowest ion stages to match other elements - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } + decay::fprint_nuc_abundances(estimators_file, mgi, globals::timesteps[timestep].mid, element); + + if (nions == 0 || elpop <= 0.) { + // dummy element for nuclear abundances only + continue; + } + + // const bool printdebug = false; + + bool assume_lte = true; + // bool per_gmpop = true; + // const bool lower_superlevel_only = false; + + // if (timestep % 10 == 0) + // { + // fprintf(estimators_file, "RRC_LTE_Nahar 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++) + // { + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(-1, T_e, element, ion, assume_lte, false, printdebug, + // lower_superlevel_only, per_gmpop, false)); + // } + // fprintf(estimators_file, "\n"); + // } + + // per_gmpop = false; + + // fprintf(estimators_file, "AlphaLTE_R*nne 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++) + // { + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(n, T_e, element, ion, assume_lte, false, printdebug, lower_superlevel_only, + // per_gmpop) * nne); + // } + // fprintf(estimators_file, "\n"); + + assume_lte = false; + + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "MA_IN_RADEXC Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); } - double elpop = 0.; + double ma_el = 0.; for (int ion = 0; ion < nions; 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::timesteps[timestep].mid, element); - - if (nions == 0 || elpop <= 0.) { - // dummy element for nuclear abundances only - continue; - } - - // const bool printdebug = false; - - bool assume_lte = true; - // bool per_gmpop = true; - // const bool lower_superlevel_only = false; - - // if (timestep % 10 == 0) - // { - // fprintf(estimators_file, "RRC_LTE_Nahar 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++) - // { - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(-1, T_e, element, ion, assume_lte, false, printdebug, - // lower_superlevel_only, per_gmpop, false)); - // } - // fprintf(estimators_file, "\n"); - // } - - // per_gmpop = false; - - // fprintf(estimators_file, "AlphaLTE_R*nne 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++) - // { - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(n, T_e, element, ion, assume_lte, false, printdebug, lower_superlevel_only, - // per_gmpop) * nne); - // } - // fprintf(estimators_file, "\n"); - - assume_lte = false; - - if constexpr (TRACK_ION_STATS && TRACK_ION_MASTATS) { - fprintf(estimators_file, "MA_IN_RADEXC Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - double ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_RADEXC); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_RADEXC); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_RADEEXC Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADDEEXC); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_OUT_RADEEXC Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADDEEXC); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_COLEXC Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLEXC); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_COLEXC Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLEXC); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_COLDEEXC Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLDEEXC); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_OUT_COLDEEXC Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLDEEXC); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_PHOTOION Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_PHOTOION); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_PHOTOION Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_PHOTOION); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_RADRECOMB Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADRECOMB); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_OUT_RADRECOMB Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_RADRECOMB); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_COLION Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLION); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_COLION Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_COLLION); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_COLRECOMB Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLRECOMB); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_OUT_COLRECOMB Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_COLLRECOMB); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_NTCOLION Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_NTCOLLION); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_NTCOLION Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_NTCOLLION); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_TOTAL Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_in_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_TOTAL); - ma_el += ma_in_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_in_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_TOTAL Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_in_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_TOTAL); + ma_el += ma_in_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_in_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_TOTAL Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_OUT_TOTAL Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_TOTAL); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_IN_INTERNAL Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + fprintf(estimators_file, "MA_IN_INTERNAL Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYIN_INTERNAL); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - fprintf(estimators_file, "MA_OUT_INTERNAL Z=%2d", get_atomicnumber(element)); - for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { - fprintf(estimators_file, " "); - } - ma_el = 0.; - for (int ion = 0; ion < nions; ion++) { - const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL); - ma_el += ma_ion; - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); - } - fprintf(estimators_file, " SUM: %9.3e\n", ma_el); - } - - // // spontaneous radiative recombination rate coefficient (may or may not include stim. recomb) - // fprintf(estimators_file, "AlphaR*nne 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 bool printdebug = false; - // // const bool printdebug = (get_atomicnumber(element) >= 26); - // - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, - // lower_superlevel_only, per_gmpop, false) * nne); - // } - // fprintf(estimators_file, "\n"); - // - // if (timestep % 10 == 0) - // { - // fprintf(estimators_file, "AlphaR_toSL*nne 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 bool printdebug = false; - // // const bool printdebug = (get_atomicnumber(element) >= 26); - // - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, true, per_gmpop, - // false) * nne); - // } - // fprintf(estimators_file, "\n"); - // } - // - // if constexpr (TRACK_ION_STATS) { - // fprintf(estimators_file, "AlphaR_MC_MA*nne 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); - // fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); - // } - // fprintf(estimators_file, "\n"); - // } - // - // if constexpr (TRACK_ION_STATS) { - // fprintf(estimators_file, "AlphaR_MC_KPKT*nne 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_KPKT); - // fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); - // } - // fprintf(estimators_file, "\n"); - // } + fprintf(estimators_file, "MA_OUT_INTERNAL Z=%2d", get_atomicnumber(element)); + for (int ionstage = 1; ionstage < get_ionstage(element, 0); ionstage++) { + fprintf(estimators_file, " "); + } + ma_el = 0.; + for (int ion = 0; ion < nions; ion++) { + const double ma_ion = get_ion_stats(mgi, element, ion, stats::ION_MACROATOM_ENERGYOUT_INTERNAL); + ma_el += ma_ion; + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), ma_ion); + } + fprintf(estimators_file, " SUM: %9.3e\n", ma_el); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "AlphaR_MC*nne 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); - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); - } - fprintf(estimators_file, "\n"); + // // spontaneous radiative recombination rate coefficient (may or may not include stim. recomb) + // fprintf(estimators_file, "AlphaR*nne 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 bool printdebug = false; + // // const bool printdebug = (get_atomicnumber(element) >= 26); + // + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, + // lower_superlevel_only, per_gmpop, false) * nne); + // } + // fprintf(estimators_file, "\n"); + // + // if (timestep % 10 == 0) + // { + // fprintf(estimators_file, "AlphaR_toSL*nne 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 bool printdebug = false; + // // const bool printdebug = (get_atomicnumber(element) >= 26); + // + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, true, per_gmpop, + // false) * nne); + // } + // fprintf(estimators_file, "\n"); + // } + // + // if constexpr (TRACK_ION_STATS) { + // fprintf(estimators_file, "AlphaR_MC_MA*nne 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); + // fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); + // } + // fprintf(estimators_file, "\n"); + // } + // + // if constexpr (TRACK_ION_STATS) { + // fprintf(estimators_file, "AlphaR_MC_KPKT*nne 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_KPKT); + // fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); + // } + // fprintf(estimators_file, "\n"); + // } + + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "AlphaR_MC*nne 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); + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), alpha_r_mc); + } + fprintf(estimators_file, "\n"); - fprintf(estimators_file, "BF_escfrac 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_abs = get_ion_stats(mgi, element, ion, stats::ION_RADRECOMB_ABSORBED); - fprintf(estimators_file, " %d: %9.3f", get_ionstage(element, ion), 1. - alpha_r_mc_abs / alpha_r_mc); - } - fprintf(estimators_file, "\n"); + fprintf(estimators_file, "BF_escfrac 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_abs = get_ion_stats(mgi, element, ion, stats::ION_RADRECOMB_ABSORBED); + fprintf(estimators_file, " %d: %9.3f", get_ionstage(element, ion), 1. - alpha_r_mc_abs / 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, " "); - } - for (int ion = 0; ion < nions; ion++) { - const double bb_emitted = get_ion_stats(mgi, element, ion, stats::ION_BOUNDBOUND_MACROATOM); - const double bb_abs = get_ion_stats(mgi, element, ion, stats::ION_BOUNDBOUND_ABSORBED); - fprintf(estimators_file, " %d: %9.3f", get_ionstage(element, ion), 1. - bb_abs / bb_emitted); - } - 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, " "); + } + for (int ion = 0; ion < nions; ion++) { + const double bb_emitted = get_ion_stats(mgi, element, ion, stats::ION_BOUNDBOUND_MACROATOM); + const double bb_abs = get_ion_stats(mgi, element, ion, stats::ION_BOUNDBOUND_ABSORBED); + fprintf(estimators_file, " %d: %9.3f", get_ionstage(element, ion), 1. - bb_abs / bb_emitted); } + fprintf(estimators_file, "\n"); + } - // stimulated recombination rate coefficient - // fprintf(estimators_file, "Alpha_stim*nne 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 bool printdebug = false; - // const bool printdebug = (get_atomicnumber(element) >= 26); - // - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, - // lower_superlevel_only, per_gmpop, true) * nne); - // } - // fprintf(estimators_file, "\n"); - - // thermal collisional recombination rate coefficient - // fprintf(estimators_file, "Alpha_C*nne 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++) - // { - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, true, printdebug, lower_superlevel_only, - // per_gmpop) * nne); - // } - // fprintf(estimators_file, "\n"); - - // if (timestep % 20 == 0) - // { - // 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 chi_bf = calculate_chi_bf_gammacontr(mgi, nu_edge, false); - // - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // chi_bf); - // } - // fprintf(estimators_file, "\n"); - // } - - // { - // fprintf(estimators_file, "gamma_R 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++) - // { - // // const bool printdebug_gammar = (get_atomicnumber(element) == 26 && get_ionstage(element, ion) == 2); - // const bool printdebug_gammar = false; - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, false, - // false)); - // } - // fprintf(estimators_file, "\n"); - // } - - if (DETAILED_BF_ESTIMATORS_ON) { - fprintf(estimators_file, "gamma_R_integral 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++) { - // const bool printdebug_gammar = (get_atomicnumber(element) == 26 && get_ionstage(element, ion) == 2); - const bool printdebug_gammar = false; - fprintf( - estimators_file, " %d: %9.3e", get_ionstage(element, ion), - calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, false, true)); - } - fprintf(estimators_file, "\n"); + // stimulated recombination rate coefficient + // fprintf(estimators_file, "Alpha_stim*nne 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 bool printdebug = false; + // const bool printdebug = (get_atomicnumber(element) >= 26); + // + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, false, printdebug, + // lower_superlevel_only, per_gmpop, true) * nne); + // } + // fprintf(estimators_file, "\n"); + + // thermal collisional recombination rate coefficient + // fprintf(estimators_file, "Alpha_C*nne 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++) + // { + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_ionrecombcoeff(mgi, T_e, element, ion, assume_lte, true, printdebug, lower_superlevel_only, + // per_gmpop) * nne); + // } + // fprintf(estimators_file, "\n"); + + // { + // fprintf(estimators_file, "gamma_R 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++) + // { + // // const bool printdebug_gammar = (get_atomicnumber(element) == 26 && get_ionstage(element, ion) == 2); + // const bool printdebug_gammar = false; + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, false, + // false)); + // } + // fprintf(estimators_file, "\n"); + // } + + if (DETAILED_BF_ESTIMATORS_ON) { + fprintf(estimators_file, "gamma_R_integral 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++) { + // const bool printdebug_gammar = (get_atomicnumber(element) == 26 && get_ionstage(element, ion) == 2); + const bool printdebug_gammar = false; + fprintf( + estimators_file, " %d: %9.3e", get_ionstage(element, ion), + calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, false, true)); } + fprintf(estimators_file, "\n"); + } - if (DETAILED_BF_ESTIMATORS_ON) { - fprintf(estimators_file, "gamma_R_bfest 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++) { - // const bool printdebug_gammar = ((get_atomicnumber(element) == 26 || get_atomicnumber(element) == 28) && - // get_ionstage(element, ion) >= 2); const bool printdebug_gammar = (get_atomicnumber(element) >= 26); - const bool printdebug_gammar = false; - fprintf( - estimators_file, " %d: %9.3e", get_ionstage(element, ion), - calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, true, false)); - } - fprintf(estimators_file, "\n"); + if (DETAILED_BF_ESTIMATORS_ON) { + fprintf(estimators_file, "gamma_R_bfest 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++) { + // const bool printdebug_gammar = ((get_atomicnumber(element) == 26 || get_atomicnumber(element) == 28) && + // get_ionstage(element, ion) >= 2); const bool printdebug_gammar = (get_atomicnumber(element) >= 26); + const bool printdebug_gammar = false; + fprintf( + estimators_file, " %d: %9.3e", get_ionstage(element, ion), + calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, false, printdebug_gammar, true, false)); + } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION)); + } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BF 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDFREE)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BF 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDFREE)); + } + fprintf(estimators_file, "\n"); + } - // fprintf(estimators_file, "gamma_R_MC_BFsameZ 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++) - // { - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFSAMEELEMENT]); - // } - // fprintf(estimators_file, "\n"); + // fprintf(estimators_file, "gamma_R_MC_BFsameZ 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++) + // { + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFSAMEELEMENT]); + // } + // fprintf(estimators_file, "\n"); + + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BF_i+1 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSONE)); + } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BF_i+1 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSONE)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BF_i+2 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSTWO)); + } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BF_i+2 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSTWO)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BF_i+3 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSTHREE)); + } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BF_i+3 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFIONPLUSTHREE)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BFtoSL 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFLOWERSUPERLEVEL)); } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BFtoSL 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBFLOWERSUPERLEVEL)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BB 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUND)); } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BB 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUND)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BB_i+1 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSONE)); } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BB_i+1 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSONE)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BB_i+2 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTWO)); } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BB_i+2 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTWO)); - } - fprintf(estimators_file, "\n"); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_R_MC_BB_i+3 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++) { + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTHREE)); } + fprintf(estimators_file, "\n"); + } - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_R_MC_BB_i+3 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_PHOTOION_FROMBOUNDBOUNDIONPLUSTHREE)); - } - fprintf(estimators_file, "\n"); + // fprintf(estimators_file, "gamma_C 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++) + // { + // fprintf(estimators_file, " %d: %9.3e", + // get_ionstage(element, ion), + // calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, true, + // printdebug)); + // } + // fprintf(estimators_file, "\n"); + + if (NT_ON) { + fprintf(estimators_file, "gamma_NT 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++) { + const double Y_nt = nonthermal::nt_ionization_ratecoeff(mgi, element, ion); + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), Y_nt); } + fprintf(estimators_file, "\n"); - // fprintf(estimators_file, "gamma_C 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++) - // { - // fprintf(estimators_file, " %d: %9.3e", - // get_ionstage(element, ion), - // calculate_iongamma_per_ionpop(mgi, T_e, element, ion, assume_lte, true, - // printdebug)); - // } - // fprintf(estimators_file, "\n"); - - if (NT_ON) { - fprintf(estimators_file, "gamma_NT Z=%2d", get_atomicnumber(element)); + if constexpr (TRACK_ION_STATS) { + fprintf(estimators_file, "gamma_NT_MC 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++) { - const double Y_nt = nonthermal::nt_ionization_ratecoeff(mgi, element, ion); - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), Y_nt); + fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), + get_ion_stats(mgi, element, ion, stats::ION_NTION)); } fprintf(estimators_file, "\n"); - - if constexpr (TRACK_ION_STATS) { - fprintf(estimators_file, "gamma_NT_MC 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++) { - fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - get_ion_stats(mgi, element, ion, stats::ION_NTION)); - } - fprintf(estimators_file, "\n"); - } } + } - if (USE_LUT_PHOTOION && globals::nbfcontinua > 0) { - fprintf(estimators_file, "corrphotoionrenorm Z=%2d", get_atomicnumber(element)); - for (int ion = 0; ion < nions - 1; ion++) { + if (USE_LUT_PHOTOION && globals::nbfcontinua_ground > 0) { + fprintf(estimators_file, "corrphotoionrenorm Z=%2d", get_atomicnumber(element)); + for (int ion = 0; ion < nions - 1; ion++) { + const int groundcontindex = globals::elements[element].ions[ion].groundcontindex; + if (groundcontindex >= 0) { fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - globals::corrphotoionrenorm[get_ionestimindex(mgi, element, ion)]); + globals::corrphotoionrenorm[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)]); } - fprintf(estimators_file, "\n"); - fprintf(estimators_file, "gammaestimator Z=%2d", get_atomicnumber(element)); - for (int ion = 0; ion < nions - 1; ion++) { + } + fprintf(estimators_file, "\n"); + fprintf(estimators_file, "gammaestimator Z=%2d", get_atomicnumber(element)); + for (int ion = 0; ion < nions - 1; ion++) { + const int groundcontindex = globals::elements[element].ions[ion].groundcontindex; + if (groundcontindex >= 0) { fprintf(estimators_file, " %d: %9.3e", get_ionstage(element, ion), - globals::gammaestimator[get_ionestimindex(mgi, element, ion)]); + globals::gammaestimator[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)]); } - fprintf(estimators_file, "\n"); } + fprintf(estimators_file, "\n"); } + } - fprintf(estimators_file, "heating: ff %11.5e bf %11.5e coll %11.5e dep %11.5e heating_dep/total_dep %.3f\n", - heatingcoolingrates->heating_ff, heatingcoolingrates->heating_bf, heatingcoolingrates->heating_collisional, - heatingcoolingrates->heating_dep, heatingcoolingrates->nt_frac_heating); - fprintf(estimators_file, "cooling: ff %11.5e fb %11.5e coll %11.5e adiabatic %11.5e\n", - heatingcoolingrates->cooling_ff, heatingcoolingrates->cooling_fb, heatingcoolingrates->cooling_collisional, - heatingcoolingrates->cooling_adiabatic); + fprintf(estimators_file, "heating: ff %11.5e bf %11.5e coll %11.5e dep %11.5e heating_dep/total_dep %.3f\n", + heatingcoolingrates->heating_ff, heatingcoolingrates->heating_bf, heatingcoolingrates->heating_collisional, + heatingcoolingrates->heating_dep, heatingcoolingrates->nt_frac_heating); + fprintf(estimators_file, "cooling: ff %11.5e fb %11.5e coll %11.5e adiabatic %11.5e\n", + heatingcoolingrates->cooling_ff, heatingcoolingrates->cooling_fb, heatingcoolingrates->cooling_collisional, + heatingcoolingrates->cooling_adiabatic); - } else { - // modelgrid cells which are not represented in the simulation grid - fprintf(estimators_file, "timestep %d modelgridindex %d EMPTYCELL\n", timestep, mgi); - } fprintf(estimators_file, "\n"); fflush(estimators_file); - const int write_estim_duration = time(nullptr) - sys_time_start_write_estimators; + const auto write_estim_duration = std::time(nullptr) - sys_time_start_write_estimators; if (write_estim_duration >= 1) { - printout("writing estimators for timestep %d cell %d took %d seconds\n", timestep, mgi, write_estim_duration); - } -} - -void cellhistory_reset(const int modelgridindex, const bool new_timestep) { - /// All entries of the cellhistory stack must be flagged as empty at the - /// onset of the new timestep. Also, boundary crossing? - /// Calculate the level populations for this cell, and flag the other entries - /// as empty. - /// Make known that globals::cellhistory[tid] contains information about the - /// cell given by cellnumber. (-99 if invalid) - if ((modelgridindex == globals::cellhistory[tid].cellnumber) && !new_timestep) { - return; - } - - const int tid = get_thread_num(); - - // force rpkt opacities to be recalculated next time they are accessed - globals::chi_rpkt_cont[tid].recalculate_required = true; - - globals::cellhistory[tid].cellnumber = modelgridindex; - - // int nlevels_with_processrates = 0; - // const double T_e = modelgridindex >= 0 ? grid ::get_Te(modelgridindex) : 0.; - const int nelements = get_nelements(); - for (int element = 0; element < nelements; element++) { - const int nions = get_nions(element); - for (int ion = 0; ion < nions; ion++) { - globals::cellhistory[tid].cooling_contrib[kpkt::get_coolinglistoffset(element, ion)] = COOLING_UNDEFINED; - - if (modelgridindex >= 0) { - const int nlevels = get_nlevels(element, ion); - for (int level = 0; level < nlevels; level++) { - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].population = - calculate_levelpop(modelgridindex, element, ion, level); - } - } - } - - for (int ion = 0; ion < nions; ion++) { - const int nlevels = get_nlevels(element, ion); - for (int level = 0; level < nlevels; level++) { - for (int phixstargetindex = 0; phixstargetindex < get_nphixstargets(element, ion, level); phixstargetindex++) { - globals::cellhistory[tid] - .chelements[element] - .chions[ion] - .chlevels[level] - .chphixstargets[phixstargetindex] - .corrphotoioncoeff = -99.; - -#if (SEPARATE_STIMRECOMB) - globals::cellhistory[tid] - .chelements[element] - .chions[ion] - .chlevels[level] - .chphixstargets[phixstargetindex] - .stimrecombcoeff = -99.; -#endif - } - /// This is the only flag needed for all of the following MA stuff! - // if - // (globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].processrates[MA_ACTION_COLDEEXC] - // >= 0) - // nlevels_with_processrates++; - - globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].processrates[MA_ACTION_COLDEEXC] = - -99.; - - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].rad_deexc = - // -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].rad_recomb = - // -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].col_recomb = - // -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].internal_down_same - // = -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].internal_up_same - // = -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].internal_down_lower - // = -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].internal_up_higher - // = -99.; - // - // ndowntrans = get_ndowntrans(element, ion, level); - // nuptrans = get_nuptrans(element, ion, level); - // for (i = 0; i < ndowntrans; i++) - // { - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].sum_epstrans_rad_deexc[i] - // = -99.; - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].individ_internal_down_same[i] - // = -99.; - // } - // for (i = 0; i < nuptrans; i++) - // { - // globals::cellhistory[tid].chelements[element].chions[ion].chlevels[level].sum_internal_up_same[i] - // = -99.; - // } - } - } - } - - if (modelgridindex >= 0) { - const int nbfcont = globals::nbfcontinua; - std::fill_n(globals::cellhistory[tid].ch_allcont_departureratios, nbfcont, -1); + printout("writing estimators for timestep %d cell %d took %ld seconds\n", timestep, mgi, write_estim_duration); } - // printout("nlevels_with_processrates %d\n", nlevels_with_processrates); - - // globals::cellhistory[tid].totalcooling = COOLING_UNDEFINED; - // globals::cellhistory[tid].phixsflag = PHIXS_UNDEFINED; } -static void solve_Te_nltepops(const int n, const int nts, const int titer, - struct heatingcoolingrates *heatingcoolingrates) -// n is the modelgridindex (TODO: rename to mgi) +void solve_Te_nltepops(const int mgi, const int nonemptymgi, const int nts, const int titer, + HeatingCoolingRates *heatingcoolingrates) // nts is the timestep number { // bfheating coefficients are needed for the T_e solver, but // they only depend on the radiation field, which is fixed during the iterations below - printout("calculate_bfheatingcoeffs for timestep %d cell %d...", nts, n); - const time_t sys_time_start_calculate_bfheatingcoeffs = time(nullptr); - calculate_bfheatingcoeffs(n); - printout("took %ld seconds\n", time(nullptr) - sys_time_start_calculate_bfheatingcoeffs); + printout("calculate_bfheatingcoeffs for timestep %d cell %d...", nts, mgi); + const auto sys_time_start_calculate_bfheatingcoeffs = std::time(nullptr); + thread_local static auto bfheatingcoeffs = std::vector(get_includedlevels()); + + calculate_bfheatingcoeffs(mgi, bfheatingcoeffs); + printout("took %ld seconds\n", std::time(nullptr) - sys_time_start_calculate_bfheatingcoeffs); const double covergence_tolerance = 0.04; for (int nlte_iter = 0; nlte_iter <= NLTEITER; nlte_iter++) { - const time_t sys_time_start_spencerfano = time(nullptr); + const auto sys_time_start_spencerfano = std::time(nullptr); if (NT_ON && NT_SOLVE_SPENCERFANO) { // SF solution depends on the ionization balance, and weakly on nne - nonthermal::solve_spencerfano(n, nts, nlte_iter); + nonthermal::solve_spencerfano(mgi, nts, nlte_iter); } - const int duration_solve_spencerfano = time(nullptr) - sys_time_start_spencerfano; + const int duration_solve_spencerfano = std::time(nullptr) - sys_time_start_spencerfano; - const time_t sys_time_start_partfuncs_or_gamma = time(nullptr); + const auto sys_time_start_partfuncs_or_gamma = std::time(nullptr); for (int element = 0; element < get_nelements(); element++) { if (!elem_has_nlte_levels(element)) { - calculate_cellpartfuncts(n, element); + calculate_cellpartfuncts(mgi, 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[get_ionestimindex(n, element, ion)] = calculate_iongamma_per_gspop(n, element, ion); + globals::gammaestimator[get_ionestimindex_nonemptymgi(nonemptymgi, element, ion)] = + calculate_iongamma_per_gspop(mgi, element, ion); } } } - const int duration_solve_partfuncs_or_gamma = time(nullptr) - sys_time_start_partfuncs_or_gamma; + const int duration_solve_partfuncs_or_gamma = std::time(nullptr) - sys_time_start_partfuncs_or_gamma; - const double prev_T_e = grid::get_Te(n); - const time_t sys_time_start_Te = time(nullptr); + const double prev_T_e = grid::get_Te(mgi); + const auto sys_time_start_Te = std::time(nullptr); const int nts_for_te = (titer == 0) ? nts - 1 : nts; /// Find T_e as solution for thermal balance - call_T_e_finder(n, nts, globals::timesteps[nts_for_te].mid, MINTEMP, MAXTEMP, heatingcoolingrates); + call_T_e_finder(mgi, nts, globals::timesteps[nts_for_te].mid, MINTEMP, MAXTEMP, heatingcoolingrates, + bfheatingcoeffs); - const int duration_solve_T_e = time(nullptr) - sys_time_start_Te; + const int duration_solve_T_e = std::time(nullptr) - sys_time_start_Te; if (globals::total_nlte_levels == 0) { - const time_t sys_time_start_pops = time(nullptr); - calculate_ion_balance_nne(n); - const int duration_solve_pops = time(nullptr) - sys_time_start_pops; + const auto sys_time_start_pops = std::time(nullptr); + calculate_ion_balance_nne(mgi); + const int duration_solve_pops = std::time(nullptr) - sys_time_start_pops; printout( "Grid solver cell %d timestep %d: time spent on: Spencer-Fano %ds, partfuncs/gamma " "%ds, T_e %ds, populations %ds\n", - n, nts, duration_solve_spencerfano, duration_solve_partfuncs_or_gamma, duration_solve_T_e, + mgi, 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 (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); + const double fracdiff_T_e = fabs((grid::get_Te(mgi) / prev_T_e) - 1); + const auto sys_time_start_nltepops = std::time(nullptr); // fractional difference between previous and current iteration's (nne or max(ground state // population change)) 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); + if (get_nions(element) > 0 && elem_has_nlte_levels(element)) { + solve_nlte_pops_element(element, mgi, nts, nlte_iter); + calculate_cellpartfuncts(mgi, element); } } - const int duration_solve_nltepops = time(nullptr) - sys_time_start_nltepops; + const int duration_solve_nltepops = std::time(nullptr) - sys_time_start_nltepops; - 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); + const double nne_prev = grid::get_nne(mgi); + calculate_ion_balance_nne(mgi); // sets nne + fracdiff_nne = fabs((grid::get_nne(mgi) / 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); + mgi, 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); + mgi, nts, nlte_iter, nne_prev, grid::get_nne(mgi), fracdiff_nne, prev_T_e, grid::get_Te(mgi), fracdiff_T_e); if (fracdiff_nne <= covergence_tolerance && fracdiff_T_e <= covergence_tolerance) { printout( @@ -869,13 +769,19 @@ static void solve_Te_nltepops(const int n, const int nts, const int titer, } } -static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, const double estimator_normfactor) { +void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, const double estimator_normfactor) { assert_always(USE_LUT_PHOTOION || USE_LUT_BFHEATING); + const int nonemptymgi = grid::get_modelcell_nonemptymgi(mgi); 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 - 1; ion++) { - const int ionestimindex = get_ionestimindex(mgi, element, ion); + const int groundcontindex = globals::elements[element].ions[ion].groundcontindex; + if (groundcontindex < 0) { + continue; + } + const int ionestimindex = nonemptymgi * globals::nbfcontinua_ground + groundcontindex; + globals::gammaestimator[ionestimindex] *= estimator_normfactor / H; #ifdef DO_TITER if (globals::gammaestimator_save[ionestimindex] >= 0) { @@ -894,7 +800,7 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, "get_corrphotoioncoeff_ana(%d,%d,%d,%d,%d)=%g/%g", element, ion, 0, 0, mgi, globals::gammaestimator[ionestimindex], get_corrphotoioncoeff_ana(element, ion, 0, 0, mgi)); - abort(); + std::abort(); } } @@ -912,7 +818,11 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, /// 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. - const int ionestimindex = get_ionestimindex(mgi, element, ion); + const int groundcontindex = globals::elements[element].ions[ion].groundcontindex; + if (groundcontindex < 0) { + continue; + } + const int ionestimindex = nonemptymgi * globals::nbfcontinua_ground + groundcontindex; if constexpr (USE_LUT_PHOTOION) { globals::gammaestimator[ionestimindex] = calculate_iongamma_per_gspop(mgi, element, ion); @@ -940,7 +850,7 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, "[fatal] about to set bfheatingestimator = NaN = bfheatingestimator / " "get_bfheatingcoeff_ana(%d,%d,%d,%d,%d)=%g/%g", element, ion, 0, 0, mgi, globals::bfheatingestimator[ionestimindex], bfheatingcoeff_ana); - abort(); + std::abort(); } } } @@ -949,252 +859,247 @@ static void update_gamma_corrphotoionrenorm_bfheating_estimators(const int mgi, } #ifdef DO_TITER -static void titer_average_estimators(const int n) { - if (globals::ffheatingestimator_save[n] >= 0) { - globals::ffheatingestimator[n] = (globals::ffheatingestimator[n] + globals::ffheatingestimator_save[n]) / 2; +static void titer_average_estimators(const int nonemptymgi) { + if (globals::ffheatingestimator_save[nonemptymgi] >= 0) { + globals::ffheatingestimator[nonemptymgi] = + (globals::ffheatingestimator[nonemptymgi] + globals::ffheatingestimator_save[nonemptymgi]) / 2; } - globals::ffheatingestimator_save[n] = globals::ffheatingestimator[n]; - if (globals::colheatingestimator_save[n] >= 0) { - globals::colheatingestimator[n] = (globals::colheatingestimator[n] + globals::colheatingestimator_save[n]) / 2; + globals::ffheatingestimator_save[nonemptymgi] = globals::ffheatingestimator[nonemptymgi]; + if (globals::colheatingestimator_save[nonemptymgi] >= 0) { + globals::colheatingestimator[nonemptymgi] = + (globals::colheatingestimator[nonemptymgi] + globals::colheatingestimator_save[nonemptymgi]) / 2; } - globals::colheatingestimator_save[n] = globals::colheatingestimator[n]; + globals::colheatingestimator_save[nonemptymgi] = globals::colheatingestimator[nonemptymgi]; } #endif -static void zero_gammaestimator(const int modelgridindex) { - assert_always(USE_LUT_PHOTOION); - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < (get_nions(element) - 1); ion++) { - globals::gammaestimator[get_ionestimindex(modelgridindex, element, ion)] = 0.; - } +void update_grid_cell(const int mgi, const int nts, const int nts_prev, const int titer, const double tratmid, + const double deltat, HeatingCoolingRates *heatingcoolingrates) { + const int assoc_cells = grid::get_numassociatedcells(mgi); + if (assoc_cells < 1) { + /// For modelgrid cells that are not represented in the simulation grid, + /// Set grid properties to zero + grid::set_TR(mgi, 0.); + grid::set_TJ(mgi, 0.); + grid::set_Te(mgi, 0.); + grid::set_W(mgi, 0.); + return; } -} -static void set_all_corrphotoionrenorm(const int modelgridindex, const double value) { - assert_always(USE_LUT_PHOTOION); - for (int element = 0; element < get_nelements(); element++) { - for (int ion = 0; ion < (get_nions(element) - 1); ion++) { - globals::corrphotoionrenorm[get_ionestimindex(modelgridindex, element, ion)] = value; - } - } -} + const auto nonemptymgi = grid::get_modelcell_nonemptymgi(mgi); -static void update_grid_cell(const int mgi, const int nts, const int nts_prev, const int titer, const double tratmid, - const double deltat, struct heatingcoolingrates *heatingcoolingrates) -// n is the modelgrid index -{ - const int assoc_cells = grid::get_numassociatedcells(mgi); - if (assoc_cells > 0) { - const double deltaV = - 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); + const double deltaV = + grid::get_modelcell_assocvolume_tmin(mgi) * pow(globals::timesteps[nts_prev].mid / globals::tmin, 3); + const auto sys_time_start_update_cell = std::time(nullptr); - printout("update_grid_cell: working on cell %d before timestep %d titeration %d...\n", mgi, nts, titer); + printout("update_grid_cell: working on cell %d before timestep %d titeration %d...\n", mgi, nts, titer); - /// Update current mass density of cell - grid::set_rho(mgi, grid::get_rho_tmin(mgi) / pow(tratmid, 3)); + /// Update current mass density of cell + grid::set_rho(mgi, grid::get_rho_tmin(mgi) / pow(tratmid, 3)); - /// Update elemental abundances with radioactive decays - decay::update_abundances(mgi, nts, globals::timesteps[nts].mid); + /// Update elemental abundances with radioactive decays + decay::update_abundances(mgi, nts, globals::timesteps[nts].mid); - const double estimator_normfactor = 1 / deltaV / deltat / globals::nprocs; - const double estimator_normfactor_over4pi = ONEOVER4PI * estimator_normfactor; + const double estimator_normfactor = 1 / deltaV / deltat / globals::nprocs; + const double estimator_normfactor_over4pi = ONEOVER4PI * estimator_normfactor; - if (globals::opacity_case < 4) { - // various forms of grey opacity - grid::modelgrid[mgi].thick = 1; + if (globals::opacity_case < 4) { + // various forms of grey opacity + grid::modelgrid[mgi].thick = 1; - 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 { - grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1)); - } + 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 { + grid::set_kappagrey(mgi, globals::opcase3_normal * (0.9 * grid::get_ffegrp(mgi) + 0.1)); } } + } - 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 (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 (USE_LUT_PHOTOION && !globals::simulation_continued_from_saved) { - /// Determine renormalisation factor for corrected photoionization cross-sections - set_all_corrphotoionrenorm(mgi, 1.); - } + if (USE_LUT_PHOTOION && !globals::simulation_continued_from_saved) { + /// Determine renormalisation factor for corrected photoionization cross-sections + std::fill_n(&globals::corrphotoionrenorm[nonemptymgi * globals::nbfcontinua_ground], globals::nbfcontinua_ground, + 1.); + } - /// 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 != 1 && 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; - } + /// 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 != 1 && grid::get_W(mgi) == 1) { + printout( + "force modelgrid cell %d to grey/LTE thick = 1 for update grid since existing W == 1. (will not have " + "gamma estimators)\n", + mgi); + grid::modelgrid[mgi].thick = 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); + printout("lte_iteration %d\n", globals::lte_iteration ? 1 : 0); + printout("mgi %d modelgrid.thick: %d (during grid update)\n", mgi, grid::modelgrid[mgi].thick); - 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 + 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 - /// 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) + /// 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); + const auto sys_time_start_temperature_corrections = std::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 + radfield::normalise_J(mgi, estimator_normfactor_over4pi); // this applies normalisation to the fullspec J + // this stores the factor that will be applied later for the J bins but not fullspec J + radfield::set_J_normfactor(nonemptymgi, estimator_normfactor_over4pi); #ifdef DO_TITER - radfield::titer_J(mgi); + radfield::titer_J(mgi); #endif - if constexpr (TRACK_ION_STATS) { - stats::normalise_ion_estimators(mgi, deltat, deltaV); - } + if constexpr (TRACK_ION_STATS) { + stats::normalise_ion_estimators(mgi, deltat, deltaV); + } - // 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) + // 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) - 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); + 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); - if constexpr (USE_LUT_PHOTOION) { - set_all_corrphotoionrenorm(mgi, 1.); - } + if constexpr (USE_LUT_PHOTOION) { + std::fill_n(&globals::corrphotoionrenorm[nonemptymgi * globals::nbfcontinua_ground], + globals::nbfcontinua_ground, 1.); + } - 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 + 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 - radfield::normalise_nuJ(mgi, estimator_normfactor_over4pi); + radfield::normalise_nuJ(mgi, estimator_normfactor_over4pi); - globals::ffheatingestimator[mgi] *= estimator_normfactor; - globals::colheatingestimator[mgi] *= estimator_normfactor; + globals::ffheatingestimator[nonemptymgi] *= estimator_normfactor; + globals::colheatingestimator[nonemptymgi] *= estimator_normfactor; #ifdef DO_TITER - radfield::titer_nuJ(mgi); - titer_average_estimators(mgi); + radfield::titer_nuJ(mgi); + titer_average_estimators(mgi); #endif - if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { - update_gamma_corrphotoionrenorm_bfheating_estimators(mgi, estimator_normfactor); - } - - // 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); + if constexpr (USE_LUT_PHOTOION || USE_LUT_BFHEATING) { + update_gamma_corrphotoionrenorm_bfheating_estimators(mgi, estimator_normfactor); + } - if constexpr (DETAILED_BF_ESTIMATORS_ON) { - radfield::normalise_bf_estimators(mgi, estimator_normfactor / H); - } + // 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); - solve_Te_nltepops(mgi, nts, titer, heatingcoolingrates); + if constexpr (DETAILED_BF_ESTIMATORS_ON) { + radfield::normalise_bf_estimators(mgi, nonemptymgi, estimator_normfactor / H); } - printout("Temperature/NLTE solution for cell %d timestep %d took %ld seconds\n", mgi, nts, - time(nullptr) - sys_time_start_temperature_corrections); + + solve_Te_nltepops(mgi, nonemptymgi, nts, titer, heatingcoolingrates); } + printout("Temperature/NLTE solution for cell %d timestep %d took %ld seconds\n", mgi, nts, + std::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; + 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); + 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; + grid::modelgrid[mgi].grey_depth = grey_optical_depth; - // grey_optical_depth = compton_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; - } + 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; + } - 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 (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 + printout("cooling rates read from gridsave file for timestep %d cell %d...", nts, mgi); + 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 auto sys_time_start_calc_kpkt_rates = std::time(nullptr); - printout("calculate_cooling_rates for timestep %d cell %d...", nts, mgi); + printout("calculating cooling_rates for timestep %d cell %d...", nts, mgi); - // don't pass pointer to heatingcoolingrates because current populations and rates weren't - // used to determine T_e - kpkt::calculate_cooling_rates(mgi, nullptr); + // don't pass pointer to heatingcoolingrates because current populations and rates weren't + // used to determine T_e + kpkt::calculate_cooling_rates(mgi, nullptr); - printout("took %ld seconds\n", time(nullptr) - sys_time_start_calc_kpkt_rates); - } + printout("took %ld seconds\n", std::time(nullptr) - sys_time_start_calc_kpkt_rates); + } - const int update_grid_cell_seconds = time(nullptr) - sys_time_start_update_cell; - if (update_grid_cell_seconds > 0) { - printout("update_grid_cell for cell %d timestep %d took %ld seconds\n", mgi, nts, update_grid_cell_seconds); + if constexpr (EXPANSIONOPACITIES_ON) { + if (grid::modelgrid[mgi].thick != 1) { + calculate_binned_opacities(mgi); } - } else { - /// For modelgrid cells that are not represented in the simulation grid, - /// Set grid properties to zero - grid::set_TR(mgi, 0.); - grid::set_TJ(mgi, 0.); - grid::set_Te(mgi, 0.); - grid::set_W(mgi, 0.); + } + + const int update_grid_cell_seconds = std::time(nullptr) - sys_time_start_update_cell; + if (update_grid_cell_seconds > 0) { + printout("update_grid_cell for cell %d timestep %d took %d seconds\n", mgi, nts, update_grid_cell_seconds); } } +} // anonymous namespace + void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const int my_rank, const int nstart, - const int ndo, const int titer, const time_t real_time_start) + const int ndo, const int titer, const std::time_t real_time_start) // Subroutine to update the matter quantities in the grid cells at the start // of the new timestep. /// nts timestep { - const time_t sys_time_start_update_grid = time(nullptr); + const auto sys_time_start_update_grid = std::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::timesteps[nts].mid / DAY); @@ -1204,7 +1109,7 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const /// unless they have been read from file if ((!globals::simulation_continued_from_saved) || (nts - globals::timestep_initial != 0) || (titer != 0)) { printout("nts %d, titer %d: reset corr photoionrenorm\n", nts, titer); - std::fill_n(globals::corrphotoionrenorm, grid::get_nonempty_npts_model() * get_includedions(), 0.); + std::fill_n(globals::corrphotoionrenorm, grid::get_nonempty_npts_model() * globals::nbfcontinua_ground, 0.); printout("after nts %d, titer %d: reset corr photoionrenorm\n", nts, titer); } } @@ -1212,14 +1117,6 @@ 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::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. - auto mps = std::make_unique(get_max_threads()); - - for (int i = 0; i < get_max_threads(); i++) { - mps[i] = 1.e35; - } - /// 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 @@ -1236,53 +1133,39 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const // printout("timestep %d, titer %d\n", nts, titer); // printout("deltat %g\n", deltat); + cellcache_change_cell(-99); + + /// Do not use values which are saved in the cellcache within update_grid + use_cellcache = false; + #ifdef _OPENMP #pragma omp parallel #endif { - /// Do not use values which are saved in the cellhistory within update_grid - /// and daughter routines (THREADPRIVATE VARIABLE, THEREFORE HERE!) - use_cellhist = false; - cellhistory_reset(-99, true); - /// Updating cell information #ifdef _OPENMP #pragma omp for schedule(dynamic) #endif - for (int mgi = 0; mgi < grid::get_npts_model(); mgi++) { + for (int mgi = nstart; mgi < nstart + ndo; mgi++) { /// Check if this task should work on the current model grid cell. /// If yes, update the cell and write out the estimators - if (mgi >= nstart && mgi < nstart + ndo) { - // use_cellhist = false; - // cellhistory_reset(-99, true); - - struct heatingcoolingrates heatingcoolingrates = {}; - update_grid_cell(mgi, nts, nts_prev, titer, tratmid, deltat, &heatingcoolingrates); + HeatingCoolingRates heatingcoolingrates{}; + update_grid_cell(mgi, nts, nts_prev, titer, tratmid, deltat, &heatingcoolingrates); - // maybe want to add omp ordered here if the modelgrid cells should be output in order - // use_cellhist = true; - // cellhistory_reset(mgi, true); + // maybe want to add omp ordered here if the modelgrid cells should be output in order #ifdef _OPENMP #pragma omp critical(estimators_file) #endif - { write_to_estimators_file(estimators_file, mgi, nts, titer, &heatingcoolingrates); } - - } else if (grid::get_numassociatedcells(mgi) > 0) { - /// else, only reset gammaestimator to zero. This allows us to do a global MPI - /// communication after update_grid to synchronize gammaestimator - /// and write a contiguous restart file with grid properties - if constexpr (USE_LUT_PHOTOION) { - zero_gammaestimator(mgi); - } - } + { write_to_estimators_file(estimators_file, mgi, nts, titer, &heatingcoolingrates); } } /// end parallel for loop over all modelgrid cells - /// Now after all the relevant taks of update_grid have been finished activate - /// the use of the cellhistory for all OpenMP tasks, in what follows (update_packets) - use_cellhist = true; } /// end OpenMP parallel section + /// Now after all the relevant taks of update_grid have been finished activate + /// the use of the cellcache for all OpenMP tasks, in what follows (update_packets) + use_cellcache = true; + // alterative way to write out estimators. this keeps the modelgrid cells in order but // heatingrates are not valid. #ifdef _OPENMP for (int n = nstart; n < nstart+nblock; n++) // { @@ -1290,18 +1173,10 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const // } // #endif - /// Assign the minimum of thread private mps to the global variable max_path_step - globals::max_path_step = mps[0]; - for (int i = 1; i < get_max_threads(); i++) { - if (mps[i] < globals::max_path_step) { - globals::max_path_step = mps[i]; - } - } - - globals::max_path_step = fmin(globals::max_path_step, globals::rmax / 10.); + globals::max_path_step = std::min(1.e35, globals::rmax / 10.); printout("max_path_step %g\n", globals::max_path_step); - const time_t time_update_grid_end_thisrank = time(nullptr); + const auto time_update_grid_end_thisrank = std::time(nullptr); printout("finished update grid on this rank at time %ld\n", time_update_grid_end_thisrank); #ifdef MPI_ON @@ -1310,6 +1185,74 @@ void update_grid(FILE *estimators_file, const int nts, const int nts_prev, const printout( "timestep %d: time after update grid for all processes %ld (rank %d took %lds, waited " "%lds, total %lds)\n", - 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); + nts, std::time(nullptr), my_rank, time_update_grid_end_thisrank - sys_time_start_update_grid, + std::time(nullptr) - time_update_grid_end_thisrank, std::time(nullptr) - sys_time_start_update_grid); } + +void cellcache_change_cell(const int modelgridindex) { + /// All entries of the cellcache stack must be flagged as empty at the + /// onset of the new timestep. Also, boundary crossing? + /// Calculate the level populations for this cell, and flag the other entries + /// as empty. + if (modelgridindex == globals::cellcache[cellcacheslotid].cellnumber) { + return; + } + + globals::cellcache[cellcacheslotid].cellnumber = modelgridindex; + globals::cellcache[cellcacheslotid].chi_ff_nnionpart = -1.; + + const int nelements = get_nelements(); + for (int element = 0; element < nelements; element++) { + const int nions = get_nions(element); + for (int ion = 0; ion < nions; ion++) { + globals::cellcache[cellcacheslotid] + .cooling_contrib[kpkt::get_coolinglistoffset(element, ion) + kpkt::get_ncoolingterms_ion(element, ion) - 1] = + COOLING_UNDEFINED; + + if (modelgridindex >= 0) { + const int nlevels = get_nlevels(element, ion); +#ifdef _OPENMP +#pragma omp parallel for +#endif + for (int level = 0; level < nlevels; level++) { + globals::cellcache[cellcacheslotid].chelements[element].chions[ion].chlevels[level].population = + calculate_levelpop(modelgridindex, element, ion, level); + } + } + } + + for (int ion = 0; ion < nions; ion++) { + const int nlevels = get_nlevels(element, ion); + for (int level = 0; level < nlevels; level++) { + const auto nphixstargets = get_nphixstargets(element, ion, level); + for (int phixstargetindex = 0; phixstargetindex < nphixstargets; phixstargetindex++) { + globals::cellcache[cellcacheslotid] + .chelements[element] + .chions[ion] + .chlevels[level] + .chphixstargets[phixstargetindex] + .corrphotoioncoeff = -99.; + +#if (SEPARATE_STIMRECOMB) + globals::cellcache[cellcacheslotid] + .chelements[element] + .chions[ion] + .chlevels[level] + .chphixstargets[phixstargetindex] + .stimrecombcoeff = -99.; +#endif + } + + globals::cellcache[cellcacheslotid] + .chelements[element] + .chions[ion] + .chlevels[level] + .processrates[MA_ACTION_INTERNALUPHIGHER] = -99.; + } + } + } + + if (modelgridindex >= 0) { + std::fill_n(globals::cellcache[cellcacheslotid].ch_allcont_departureratios, globals::nbfcontinua, -1); + } +} \ No newline at end of file diff --git a/update_grid.h b/update_grid.h index c511db41e..e6e753171 100644 --- a/update_grid.h +++ b/update_grid.h @@ -1,3 +1,4 @@ +#pragma once #ifndef UPDATE_GRID_H #define UPDATE_GRID_H @@ -6,6 +7,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 cellhistory_reset(int modelgridindex, bool new_timestep); +void cellcache_change_cell(int modelgridindex); #endif // UPDATE_GRID_H diff --git a/update_packets.cc b/update_packets.cc index e13ee7ca4..a09610ba9 100644 --- a/update_packets.cc +++ b/update_packets.cc @@ -1,9 +1,20 @@ #include "update_packets.h" +#ifdef MPI_ON +#include +#endif + #include +#include +#include +#include +#include +#include "artisoptions.h" +#include "constants.h" #include "decay.h" #include "gammapkt.h" +#include "globals.h" #include "grid.h" #include "kpkt.h" #include "nonthermal.h" @@ -14,15 +25,32 @@ #include "update_grid.h" #include "vectors.h" -static void do_nonthermal_predeposit(struct packet *pkt_ptr, const int nts, const double t2) { - if constexpr (!INSTANT_PARTICLE_DEPOSITION) { - const double ts = pkt_ptr->prop_time; - const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); - const double rho = grid::get_rho(mgi); +namespace { + +void do_nonthermal_predeposit(Packet &pkt, const int nts, const double t2) { + double en_deposited = pkt.e_cmf; + + if constexpr (INSTANT_PARTICLE_DEPOSITION) { + // absorption happens + pkt.type = TYPE_NTLEPTON; + } else { + const double rho = grid::get_rho(grid::get_cell_modelgridindex(pkt.where)); // endot is energy loss rate (positive) in [erg/s] // endot [erg/s] from Barnes et al. (2016). see their figure 6. - const double endot = (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_ALPHA) ? 5.e11 * MEV * rho : 4.e10 * MEV * rho; + const double endot = (pkt.pellet_decaytype == decay::DECAYTYPE_ALPHA) ? 5.e11 * MEV * rho : 4.e10 * MEV * rho; + + const double ts = pkt.prop_time; + const double particle_en = H * pkt.nu_cmf; // energy of the particles in the packet + + // for endot independent of energy, the next line is trival (for E dependent endot, an integral would be needed) + + const double t_enzero = ts + particle_en / endot; // time at which zero energy is reached + if (t_enzero > t2) { + en_deposited = pkt.e_cmf * (t2 - ts) / (particle_en / endot); + } else { + en_deposited = pkt.e_cmf * (t_enzero - ts) / (particle_en / endot); + } // A discrete absorption event should occur somewhere along the // continuous track from initial kinetic energy to zero KE. @@ -30,92 +58,76 @@ static void do_nonthermal_predeposit(struct packet *pkt_ptr, const int nts, cons // endot(E) * delta_t = endot(E) * delta_E / endot(E) = delta_E (delta_t is the time spent in the bin range) // so all final energies are equally likely. // Choose random en_absorb [0, particle_en] - const double zrand = rng_uniform(); - const double particle_en = H * pkt_ptr->nu_cmf; - const double en_absorb = zrand * particle_en; + const double rnd_en_absorb = rng_uniform() * particle_en; + const double t_absorb = ts + rnd_en_absorb / endot; - // for endot independent of energy, the next line is trival (for E dependent endot, an integral would be needed) - const double t_absorb = ts + en_absorb / endot; - - // const double deltat_zeroen = particle_en / endot; - // const double t_sim_zeroen = ts + deltat_zeroen; - // printout("%s packet: nts %d energy_mev %g ts %g deltat_zeroen %g t_sim_zeroen %g t2 %g t_absorb %g\n", - // pkt_ptr->pellet_decaytype == decay::DECAYTYPE_ALPHA ? "alpha" : "beta", nts, - // particle_en / MEV, - // ts / 86400, - // deltat_zeroen / 86400, t_sim_zeroen / 86400, t2 / 86400, t_absorb / 86400); - - if (t_absorb > t2) { - // absorption happens beyond the end of the current timestep, - // so reduce the particle energy for the end of this timestep - pkt_ptr->nu_cmf = (particle_en - endot * (t2 - ts)) / H; - vec_scale(pkt_ptr->pos, t2 / ts); - pkt_ptr->prop_time = t2; - return; + // if absorption happens beyond the end of the current timestep, + // just reduce the particle energy up to the end of this timestep + const auto t_new = std::min(t_absorb, t2); + + if (t_absorb <= t2) { + pkt.type = TYPE_NTLEPTON; + } else { + pkt.nu_cmf = (particle_en - endot * (t_new - ts)) / H; } - // absorption happen part way through this timestep - vec_scale(pkt_ptr->pos, t_absorb / ts); - pkt_ptr->prop_time = t_absorb; + pkt.pos = vec_scale(pkt.pos, t_new / ts); + pkt.prop_time = t_new; } - // absorption happens - - if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_ALPHA) { - safeadd(globals::timesteps[nts].alpha_dep, pkt_ptr->e_cmf); - } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAMINUS) { - safeadd(globals::timesteps[nts].electron_dep, pkt_ptr->e_cmf); - } else if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { - safeadd(globals::timesteps[nts].positron_dep, pkt_ptr->e_cmf); + if (pkt.pellet_decaytype == decay::DECAYTYPE_ALPHA) { + atomicadd(globals::timesteps[nts].alpha_dep, en_deposited); + } else if (pkt.pellet_decaytype == decay::DECAYTYPE_BETAMINUS) { + atomicadd(globals::timesteps[nts].electron_dep, en_deposited); + } else if (pkt.pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { + atomicadd(globals::timesteps[nts].positron_dep, en_deposited); } - - pkt_ptr->type = TYPE_NTLEPTON; } -static void update_pellet(struct packet *pkt_ptr, const int nts, const double t2) { +void update_pellet(Packet &pkt, const int nts, const double t2) { // Handle inactive pellets. Need to do two things (a) check if it // decays in this time step and if it does handle that. (b) if it doesn't decay in // this time step then just move the packet along with the matter for the // start of the next time step. - assert_always(pkt_ptr->prop_time < t2); - const double ts = pkt_ptr->prop_time; + assert_always(pkt.prop_time < t2); + const double ts = pkt.prop_time; - const double tdecay = pkt_ptr->tdecay; // after packet_init(), this value never changes + const double tdecay = pkt.tdecay; // after packet_init(), this value never changes if (tdecay > t2) { // It won't decay in this timestep, so just need to move it on with the flow. - vec_scale(pkt_ptr->pos, t2 / ts); - pkt_ptr->prop_time = t2; + pkt.pos = vec_scale(pkt.pos, t2 / ts); + pkt.prop_time = t2; // That's all that needs to be done for the inactive pellet. } else if (tdecay > ts) { // The packet decays in the current timestep. - globals::timesteps[nts].pellet_decays++; + atomicadd(globals::timesteps[nts].pellet_decays, 1); - pkt_ptr->prop_time = tdecay; - vec_scale(pkt_ptr->pos, tdecay / ts); + pkt.prop_time = tdecay; + pkt.pos = vec_scale(pkt.pos, tdecay / ts); - if (pkt_ptr->originated_from_particlenotgamma) // will decay to non-thermal particle + if (pkt.originated_from_particlenotgamma) // will decay to non-thermal particle { - if (pkt_ptr->pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { - 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::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::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; + if (pkt.pellet_decaytype == decay::DECAYTYPE_BETAPLUS) { + atomicadd(globals::timesteps[nts].positron_dep, pkt.e_cmf); + pkt.type = TYPE_NTLEPTON; + pkt.absorptiontype = -10; + } else if (pkt.pellet_decaytype == decay::DECAYTYPE_BETAMINUS) { + atomicadd(globals::timesteps[nts].electron_emission, pkt.e_cmf); + pkt.em_time = pkt.prop_time; + pkt.type = TYPE_NONTHERMAL_PREDEPOSIT; + pkt.absorptiontype = -10; + } else if (pkt.pellet_decaytype == decay::DECAYTYPE_ALPHA) { + atomicadd(globals::timesteps[nts].alpha_emission, pkt.e_cmf); + pkt.em_time = pkt.prop_time; + pkt.type = TYPE_NONTHERMAL_PREDEPOSIT; + pkt.absorptiontype = -10; } } else { - safeadd(globals::timesteps[nts].gamma_emission, pkt_ptr->e_cmf); + atomicadd(globals::timesteps[nts].gamma_emission, pkt.e_cmf); // decay to gamma-ray, kpkt, or ntlepton - gammapkt::pellet_gamma_decay(pkt_ptr); + gammapkt::pellet_gamma_decay(pkt); } } else if ((tdecay > 0) && (nts == 0)) { // These are pellets whose decay times were before the first time step @@ -124,84 +136,84 @@ static void update_pellet(struct packet *pkt_ptr, const int nts, const double t2 // The position is already set at globals::tmin so don't need to move it. Assume // that it is fixed in place from decay to globals::tmin - i.e. short mfp. - pkt_ptr->e_cmf *= tdecay / globals::tmin; - pkt_ptr->type = TYPE_PRE_KPKT; - pkt_ptr->absorptiontype = -7; + pkt.e_cmf *= tdecay / globals::tmin; + pkt.type = TYPE_PRE_KPKT; + pkt.absorptiontype = -7; stats::increment(stats::COUNTER_K_STAT_FROM_EARLIERDECAY); // printout("already decayed packets and propagation by packet_prop\n"); - pkt_ptr->prop_time = globals::tmin; + pkt.prop_time = globals::tmin; + } else if constexpr (TESTMODE) { + printout("ERROR: Something wrong with decaying pellets. tdecay %g ts %g (ts + tw) %g\n", tdecay, ts, t2); + assert_testmodeonly(false); } else { - printout("ERROR: Something gone wrong with decaying pellets. tdecay %g ts %g (ts + tw) %g\n", tdecay, ts, t2); - abort(); + __builtin_unreachable(); } } -static void do_packet(struct packet *const pkt_ptr, const double t2, const int nts) +void do_packet(Packet &pkt, const double t2, const int nts) // update a packet no further than time t2 { - const int pkt_type = pkt_ptr->type; - - switch (pkt_type) { + switch (pkt.type) { case TYPE_RADIOACTIVE_PELLET: { - update_pellet(pkt_ptr, nts, t2); + update_pellet(pkt, nts, t2); break; } case TYPE_GAMMA: { - gammapkt::do_gamma(pkt_ptr, t2); + gammapkt::do_gamma(pkt, t2); - if (pkt_ptr->type != TYPE_GAMMA && pkt_ptr->type != TYPE_ESCAPE) { - safeadd(globals::timesteps[nts].gamma_dep, pkt_ptr->e_cmf); + if (pkt.type != TYPE_GAMMA && pkt.type != TYPE_ESCAPE) { + atomicadd(globals::timesteps[nts].gamma_dep, pkt.e_cmf); } break; } case TYPE_RPKT: { - do_rpkt(pkt_ptr, t2); + do_rpkt(pkt, t2); - if (pkt_ptr->type == TYPE_ESCAPE) { - safeadd(globals::timesteps[nts].cmf_lum, pkt_ptr->e_cmf); + if (pkt.type == TYPE_ESCAPE) { + atomicadd(globals::timesteps[nts].cmf_lum, pkt.e_cmf); } break; } case TYPE_NONTHERMAL_PREDEPOSIT: { - do_nonthermal_predeposit(pkt_ptr, nts, t2); + do_nonthermal_predeposit(pkt, nts, t2); break; } case TYPE_NTLEPTON: { - nonthermal::do_ntlepton(pkt_ptr); + nonthermal::do_ntlepton(pkt); break; } case TYPE_PRE_KPKT: { - kpkt::do_kpkt_blackbody(pkt_ptr); + kpkt::do_kpkt_blackbody(pkt); break; } case TYPE_KPKT: { - if (grid::modelgrid[grid::get_cell_modelgridindex(pkt_ptr->where)].thick == 1) { - kpkt::do_kpkt_blackbody(pkt_ptr); + if (grid::modelgrid[grid::get_cell_modelgridindex(pkt.where)].thick == 1 || EXPANSIONOPACITIES_ON) { + kpkt::do_kpkt_blackbody(pkt); } else { - kpkt::do_kpkt(pkt_ptr, t2, nts); + kpkt::do_kpkt(pkt, t2, nts); } break; } - case TYPE_MA: { - do_macroatom(pkt_ptr, nts); - break; + default: { + if constexpr (TESTMODE) { + printout("ERROR: Unknown packet type %d\n", pkt.type); + assert_testmodeonly(false); + } else { + __builtin_unreachable(); + } } - - default: - printout("packet_prop: Unknown packet type %d. Abort.\n", pkt_ptr->type); - abort(); } } -static auto std_compare_packets_bymodelgriddensity(const struct packet &p1, const struct packet &p2) -> bool { +auto std_compare_packets_bymodelgriddensity(const Packet &p1, const Packet &p2) -> bool { // return true if packet p1 goes before p2 // move escaped packets to the end of the list for better performance @@ -211,21 +223,33 @@ static auto std_compare_packets_bymodelgriddensity(const struct packet &p1, cons if (!esc1 && esc2) { return true; } - if (esc1 && !esc2) { - return false; - } - if (esc1 && esc2) { + if (esc1) { return false; } + // const auto ts_end = globals::timesteps[globals::timestep].start + globals::timesteps[globals::timestep].width; + + // const bool pktdone1 = (p1.prop_time >= ts_end); + // const bool pktdone2 = (p2.prop_time >= ts_end); + + // if (!pktdone1 && pktdone2) { + // return true; + // } + // if (pktdone1) { + // return false; + // } + // for both non-escaped packets, order by descending cell density const int mgi1 = grid::get_cell_modelgridindex(p1.where); const int mgi2 = grid::get_cell_modelgridindex(p2.where); - if (grid::get_rho(mgi1) > grid::get_rho(mgi2)) { + const auto rho1 = mgi1 < grid::get_npts_model() ? grid::get_rho(mgi1) : 0.0; + const auto rho2 = mgi2 < grid::get_npts_model() ? grid::get_rho(mgi2) : 0.0; + + if (rho1 > rho2) { return true; } - if (grid::get_rho(mgi1) == grid::get_rho(mgi2) && (mgi1 < mgi2)) { + if (rho1 == rho2 && (mgi1 < mgi2)) { return true; } @@ -242,97 +266,106 @@ static auto std_compare_packets_bymodelgriddensity(const struct packet &p1, cons return false; } -void update_packets(const int my_rank, const int nts, struct packet *packets) +void do_cell_packet_updates(std::span packets, const int nts, const double ts_end) { + auto update_packet = [ts_end, nts](auto &pkt) { + const int mgi = grid::get_cell_modelgridindex(pkt.where); + int newmgi = mgi; + while (pkt.prop_time < ts_end && pkt.type != TYPE_ESCAPE && (newmgi == mgi || newmgi == grid::get_npts_model())) { + do_packet(pkt, ts_end, nts); + newmgi = grid::get_cell_modelgridindex(pkt.where); + } + }; + +#if defined(STDPAR_ON) || !defined(_OPENMP) + std::for_each(EXEC_PAR packets.begin(), packets.end(), update_packet); +#else +#ifdef GPU_ON +#pragma omp target teams distribute parallel for +#else +#pragma omp parallel for schedule(nonmonotonic : dynamic) +#endif + for (ptrdiff_t i = 0; i < std::ssize(packets); i++) { + update_packet(packets[i]); + } +#endif +} + +} // anonymous namespace + +void update_packets(const int my_rank, const int nts, std::span packets) // Subroutine to move and update packets during the current timestep (nts) { // At the start, the packets have all either just been initialised or have already been // processed for one or more timesteps. Those that are pellets will just be sitting in the // matter. Those that are photons (or one sort or another) will already have a position and // a direction. - const double ts = globals::timesteps[nts].start; const double tw = globals::timesteps[nts].width; + const double ts_end = ts + tw; - const time_t time_update_packets_start = time(nullptr); + const auto time_update_packets_start = std::time(nullptr); printout("timestep %d: start update_packets at time %ld\n", nts, time_update_packets_start); bool timestepcomplete = false; int passnumber = 0; while (!timestepcomplete) { - timestepcomplete = true; // will be set false if any packets did not finish propagating in this pass - - const time_t sys_time_start_pass = time(nullptr); + const auto sys_time_start_pass = std::time(nullptr); // printout("sorting packets..."); - std::sort(packets, packets + globals::npkts, std_compare_packets_bymodelgriddensity); + std::sort(std::begin(packets), std::end(packets), std_compare_packets_bymodelgriddensity); - // printout("took %lds\n", time(nullptr) - sys_time_start_pass); + // printout("took %lds\n", std::time(nullptr) - sys_time_start_pass); printout(" update_packets timestep %d pass %3d: started at %ld\n", nts, passnumber, sys_time_start_pass); - int count_pktupdates = 0; + const int count_pktupdates = static_cast(std::ranges::count_if( + packets, [ts_end](const auto &pkt) { return pkt.prop_time < ts_end && pkt.type != TYPE_ESCAPE; })); const int updatecellcounter_beforepass = stats::get_counter(stats::COUNTER_UPDATECELL); + auto *packetgroupstart = packets.data(); -#ifdef _OPENMP -#pragma omp parallel for schedule(nonmonotonic : dynamic) -#endif - for (int n = 0; n < globals::npkts; n++) { - struct packet *pkt_ptr = &packets[n]; - - // if (pkt_ptr->type == TYPE_ESCAPE) - // { - // printout("packet index %d already escaped. Skipping rest of packets (which are all escaped).\n", n); - // // for (int n2 = n; n2 < globals::npkts; n2++) - // // { - // // assert_always(packets[n2].type == TYPE_ESCAPE); - // // } - // break; - // } - // pkt_ptr->timestep = nts; - - if (passnumber == 0) { - pkt_ptr->interactions = 0; - } - - if (pkt_ptr->type != TYPE_ESCAPE && pkt_ptr->prop_time < (ts + tw)) { - const int cellindex = pkt_ptr->where; - 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 && - grid::modelgrid[mgi].thick != 1) { - stats::increment(stats::COUNTER_UPDATECELL); - cellhistory_reset(mgi, false); - } + for (auto &pkt : packets) { + if ((pkt.type != TYPE_ESCAPE && pkt.prop_time < ts_end)) { + const int mgi = grid::get_cell_modelgridindex(pkt.where); + const bool cellcache_change_cell_required = + (mgi != grid::get_npts_model() && globals::cellcache[cellcacheslotid].cellnumber != mgi && + grid::modelgrid[mgi].thick != 1); - // enum packet_type oldtype = pkt_ptr->type; - int newmgi = mgi; - bool workedonpacket = false; - while ((newmgi == mgi || newmgi == grid::get_npts_model()) && pkt_ptr->prop_time < (ts + tw) && - pkt_ptr->type != TYPE_ESCAPE) { - workedonpacket = true; - do_packet(pkt_ptr, ts + tw, nts); - const int newcellnum = pkt_ptr->where; - newmgi = grid::get_cell_modelgridindex(newcellnum); - } - count_pktupdates += workedonpacket ? 1 : 0; + if (cellcache_change_cell_required) { + if (packetgroupstart != &pkt) { + do_cell_packet_updates(std::span(packetgroupstart, std::distance(packetgroupstart, &pkt)), nts, ts_end); + } - if (pkt_ptr->type != TYPE_ESCAPE && pkt_ptr->prop_time < (ts + tw)) { - timestepcomplete = false; +#ifdef _OPENMP +#pragma omp critical(cellchange) +#endif + { + stats::increment(stats::COUNTER_UPDATECELL); + cellcache_change_cell(mgi); + } + packetgroupstart = &pkt; } } } - const int cellhistresets = stats::get_counter(stats::COUNTER_UPDATECELL) - updatecellcounter_beforepass; + const auto packets_remaining = std::distance(packetgroupstart, packets.data() + packets.size()); + if (packets_remaining > 0) { + do_cell_packet_updates(std::span(packetgroupstart, packets_remaining), nts, ts_end); + } + + timestepcomplete = std::ranges::all_of( + packets, [ts_end](const auto &pkt) { return pkt.prop_time >= ts_end || pkt.type == TYPE_ESCAPE; }); + + const int cellcacheresets = stats::get_counter(stats::COUNTER_UPDATECELL) - updatecellcounter_beforepass; printout( - " update_packets timestep %d pass %3d: finished at %ld packetsupdated %7d cellhistoryresets %7d (took %lds)\n", - nts, passnumber, time(nullptr), count_pktupdates, cellhistresets, time(nullptr) - sys_time_start_pass); + " update_packets timestep %d pass %3d: finished at %ld packetsupdated %7d cellcacheresets %7d (took %lds)\n", + nts, passnumber, std::time(nullptr), count_pktupdates, cellcacheresets, + std::time(nullptr) - sys_time_start_pass); passnumber++; } - stats::pkt_action_counters_printout(packets, nts); + stats::pkt_action_counters_printout(nts); - const time_t time_update_packets_end_thisrank = time(nullptr); + const auto time_update_packets_end_thisrank = std::time(nullptr); printout("timestep %d: end of update_packets for this rank at time %ld\n", nts, time_update_packets_end_thisrank); #ifdef MPI_ON @@ -340,6 +373,6 @@ void update_packets(const int my_rank, const int nts, struct packet *packets) #endif printout( "timestep %d: time after update packets for all processes %ld (rank %d took %lds, waited %lds, total %lds)\n", - nts, time(nullptr), my_rank, time_update_packets_end_thisrank - time_update_packets_start, - time(nullptr) - time_update_packets_end_thisrank, time(nullptr) - time_update_packets_start); + nts, std::time(nullptr), my_rank, time_update_packets_end_thisrank - time_update_packets_start, + std::time(nullptr) - time_update_packets_end_thisrank, std::time(nullptr) - time_update_packets_start); } diff --git a/update_packets.h b/update_packets.h index d5b1bfd58..da9667e4f 100644 --- a/update_packets.h +++ b/update_packets.h @@ -1,8 +1,11 @@ +#pragma once #ifndef UPDATE_PACKETS_H #define UPDATE_PACKETS_H +#include + #include "packet.h" -void update_packets(int my_rank, int nts, struct packet *packets); +void update_packets(int my_rank, int nts, std::span packets); #endif // UPDATE_PACKETS_H diff --git a/vectors.cc b/vectors.cc deleted file mode 100644 index 4b7fd03dc..000000000 --- a/vectors.cc +++ /dev/null @@ -1,67 +0,0 @@ -#include "vectors.h" - -#include - -#include "artisoptions.h" -#include "sn3d.h" - -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 - // is parallel to z-hat. - - const double zrand = rng_uniform(); - const double phi = zrand * 2 * PI; - - const double sin_theta_sq = 1. - (cos_theta * cos_theta); - const double sin_theta = std::sqrt(sin_theta_sq); - const double zprime = cos_theta; - const double xprime = sin_theta * cos(phi); - const double yprime = sin_theta * sin(phi); - - // Now need to derotate the coordinates back to real x,y,z. - // 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. / vec_len(dir_in); - - const double r11 = dir_in[1] * norm1; - const double r12 = -1 * dir_in[0] * norm1; - const double r13 = 0.0; - const double r21 = dir_in[0] * dir_in[2] * norm1 * norm2; - const double r22 = dir_in[1] * dir_in[2] * norm1 * norm2; - const double r23 = -1 * norm2 / norm1; - const double r31 = dir_in[0] * norm2; - const double r32 = dir_in[1] * norm2; - const double r33 = dir_in[2] * norm2; - - dir_out[0] = (r11 * xprime) + (r21 * yprime) + (r31 * zprime); - dir_out[1] = (r12 * xprime) + (r22 * yprime) + (r32 * zprime); - dir_out[2] = (r13 * xprime) + (r23 * yprime) + (r33 * zprime); - - assert_testmodeonly(std::fabs(vec_len(dir_out) - 1.) < 1e-10); -} - -void get_rand_isotropic_unitvec(std::span vecout) -// Assuming isotropic distribution, get a random direction vector -{ - // alternatively, use GSL's functions: - // gsl_ran_dir_3d(rng, &vecout[0], &vecout[1], &vecout[2]); - // or - // gsl_ran_dir_nd(rng, 3, vecout); - // but check validity first - - const double zrand = rng_uniform(); - const double zrand2 = rng_uniform(); - - const double mu = -1 + (2. * zrand); - const double phi = zrand2 * 2 * PI; - const double sintheta = std::sqrt(1. - (mu * mu)); - - vecout[0] = sintheta * std::cos(phi); - vecout[1] = sintheta * std::sin(phi); - vecout[2] = mu; - - assert_testmodeonly(std::fabs(vec_len(vecout) - 1.) < 1e-10); -} diff --git a/vectors.h b/vectors.h index 3689104da..99f03f678 100644 --- a/vectors.h +++ b/vectors.h @@ -1,6 +1,8 @@ +#pragma once #ifndef VECTORS_H #define VECTORS_H +#include #include #include #include @@ -11,10 +13,8 @@ #include "packet.h" #include "sn3d.h" -void scatter_dir(std::span dir_in, double cos_theta, std::span dir_out); -void get_rand_isotropic_unitvec(std::span vecout); - -[[nodiscard]] [[gnu::pure]] constexpr auto vec_len(std::span vec) -> double +template +[[nodiscard]] [[gnu::const]] constexpr auto vec_len(const std::array vec) -> double // return the the magnitude of a vector { const double squaredlen = std::accumulate(vec.begin(), vec.end(), 0., [](auto a, auto b) { return a + b * b; }); @@ -22,52 +22,44 @@ void get_rand_isotropic_unitvec(std::span vecout); return std::sqrt(squaredlen); } -constexpr void vec_norm(std::span vec_in, std::span vec_out) -// normalizing a copy of vec_in and save it to vec_out +[[nodiscard]] [[gnu::const]] constexpr auto vec_norm(const std::array vec_in) +// get a normalized copy of vec_in { const double magnitude = vec_len(vec_in); - - vec_out[0] = vec_in[0] / magnitude; - vec_out[1] = vec_in[1] / magnitude; - vec_out[2] = vec_in[2] / magnitude; + const auto vec_out = std::array{vec_in[0] / magnitude, vec_in[1] / magnitude, vec_in[2] / magnitude}; assert_testmodeonly(fabs(vec_len(vec_out) - 1.) < 1.e-10); + return vec_out; } -[[nodiscard]] [[gnu::pure]] constexpr auto dot(std::span x, std::span y) -> double +template +[[nodiscard]] [[gnu::const]] constexpr auto dot(const std::array x, + const std::array y) -> double // vector dot product { return std::inner_product(x.begin(), x.end(), y.begin(), 0.); } -constexpr void get_velocity(std::span x, std::span y, const double t) +[[nodiscard]] [[gnu::pure]] constexpr auto get_velocity(std::span x, + const double t) -> std::array // Routine for getting velocity vector of the flow at a position with homologous expansion. { - y[0] = x[0] / t; - y[1] = x[1] / t; - y[2] = x[2] / t; + return std::array{x[0] / t, x[1] / t, x[2] / t}; } -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]); +[[nodiscard]] [[gnu::const]] constexpr auto cross_prod(const std::array vec_a, + const std::array vec_b) { + return std::array{(vec_a[1] * vec_b[2]) - (vec_b[1] * vec_a[2]), + (vec_a[2] * vec_b[0]) - (vec_b[2] * vec_a[0]), + (vec_a[0] * vec_b[1]) - (vec_b[0] * vec_a[1])}; } -constexpr void vec_scale(std::span vec, const double scalefactor) { - vec[0] *= scalefactor; - vec[1] *= scalefactor; - vec[2] *= scalefactor; +[[nodiscard]] [[gnu::const]] constexpr auto vec_scale(const std::array vec, const double scalefactor) { + return std::array{vec[0] * scalefactor, vec[1] * scalefactor, vec[2] * scalefactor}; } -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(std::span dir1, std::span vel, std::span dir2) +[[nodiscard]] [[gnu::const]] constexpr auto angle_ab(const std::array dir1, + const std::array vel) -> std::array // aberation of angles in special relativity // dir1: direction unit vector in frame1 // vel: velocity of frame2 relative to frame1 @@ -80,15 +72,14 @@ constexpr void angle_ab(std::span dir1, std::span dir2{(dir1[0] - (vel[0] * fact2)) / fact1, (dir1[1] - (vel[1] * fact2)) / fact1, + (dir1[2] - (vel[2] * fact2)) / fact1}; - vec_norm(dir2, dir2); + return vec_norm(dir2); } -[[gnu::pure]] [[nodiscard]] constexpr double doppler_nucmf_on_nurf(std::span dir_rf, - std::span vel_rf) +[[gnu::const]] [[nodiscard]] constexpr auto doppler_nucmf_on_nurf(const std::array dir_rf, + const std::array vel_rf) -> double // Doppler factor // arguments: // dir_rf: the rest frame direction (unit vector) of light propagation @@ -114,9 +105,9 @@ constexpr void angle_ab(std::span dir1, std::span pos_rf, - std::span dir_rf, - const double prop_time) +[[gnu::const]] [[nodiscard]] constexpr auto doppler_squared_nucmf_on_nurf(const std::array &pos_rf, + const std::array dir_rf, + const double prop_time) -> double // Doppler factor squared, either to first order v/c or fully relativisitic // depending on USE_RELATIVISTIC_DOPPLER_SHIFT // @@ -127,23 +118,15 @@ constexpr void angle_ab(std::span dir1, std::span vel_rf = {0, 0, 0}; // homologous flow velocity - get_velocity(pos_rf, vel_rf, prop_time); + const auto vel_rf = get_velocity(pos_rf, prop_time); assert_testmodeonly(dot(vel_rf, vel_rf) / CLIGHTSQUARED >= 0.); assert_testmodeonly(dot(vel_rf, vel_rf) / CLIGHTSQUARED < 1.); - const double ndotv = dot(dir_rf, vel_rf); - double dopplerfactorsq = 1.; - - if (USE_RELATIVISTIC_DOPPLER_SHIFT) { - const double betasq = dot(vel_rf, vel_rf) / CLIGHTSQUARED; - assert_testmodeonly(betasq >= 0.); // v < c - assert_testmodeonly(betasq < 1.); // v < c - dopplerfactorsq = std::pow(1. - (ndotv / CLIGHT), 2) / (1 - betasq); - } else { - dopplerfactorsq = 1. - 2 * (ndotv / CLIGHT); - } + const double ndotv_on_c = dot(dir_rf, vel_rf) / CLIGHT; + double dopplerfactorsq = USE_RELATIVISTIC_DOPPLER_SHIFT + ? std::pow(1. - ndotv_on_c, 2) / (1 - (dot(vel_rf, vel_rf) / CLIGHTSQUARED)) + : (1. - 2 * ndotv_on_c); assert_testmodeonly(std::isfinite(dopplerfactorsq)); assert_testmodeonly(dopplerfactorsq > 0); @@ -151,86 +134,79 @@ constexpr void angle_ab(std::span dir1, std::span pos_rf, - std::span dir_rf, - const double prop_time) { - double flow_velocity[3] = {0, 0, 0}; // homologous flow velocity - get_velocity(pos_rf, flow_velocity, prop_time); - return doppler_nucmf_on_nurf(dir_rf, flow_velocity); +[[gnu::pure]] [[nodiscard]] constexpr auto doppler_packet_nucmf_on_nurf(std::span pos_rf, + const std::array dir_rf, + const double prop_time) -> double { + return doppler_nucmf_on_nurf(dir_rf, get_velocity(pos_rf, prop_time)); } -constexpr void move_pkt(struct packet *pkt_ptr, const double distance) +constexpr auto move_pkt_withtime(std::span pos_rf, const std::array dir_rf, double &prop_time, + const double nu_rf, double &nu_cmf, const double e_rf, double &e_cmf, + const double distance) -> double /// Subroutine to move a packet along a straight line (specified by current /// dir vector). The distance moved is in the rest frame. { - /// First update pos. assert_always(distance >= 0); - pkt_ptr->pos[0] += (pkt_ptr->dir[0] * distance); - pkt_ptr->pos[1] += (pkt_ptr->dir[1] * distance); - pkt_ptr->pos[2] += (pkt_ptr->dir[2] * distance); + const double nu_cmf_old = nu_cmf; + prop_time += distance / CLIGHT_PROP; + + pos_rf[0] += (dir_rf[0] * distance); + pos_rf[1] += (dir_rf[1] * distance); + pos_rf[2] += (dir_rf[2] * 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->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; -} + const double dopplerfactor = doppler_packet_nucmf_on_nurf(pos_rf, dir_rf, prop_time); -constexpr void move_pkt_withtime(struct packet *pkt_ptr, const double distance) -/// Subroutine to move a packet along a straight line (specified by current -/// dir vector). The distance moved is in the rest frame. -{ - const double nu_cmf_old = pkt_ptr->nu_cmf; - pkt_ptr->prop_time += distance / CLIGHT_PROP; - move_pkt(pkt_ptr, distance); + nu_cmf = nu_rf * dopplerfactor; + e_cmf = e_rf * dopplerfactor; // frequency should only over decrease due to packet movement // enforce this to overcome numerical error - pkt_ptr->nu_cmf = std::min(pkt_ptr->nu_cmf, nu_cmf_old); + nu_cmf = std::min(nu_cmf, nu_cmf_old); + + return dopplerfactor; } -[[nodiscard]] [[gnu::pure]] constexpr auto get_arrive_time(const struct packet *const pkt_ptr) -> double +constexpr auto move_pkt_withtime(Packet &pkt, const double distance) -> double { + return move_pkt_withtime(pkt.pos, pkt.dir, pkt.prop_time, pkt.nu_rf, pkt.nu_cmf, pkt.e_rf, pkt.e_cmf, distance); +} + +[[nodiscard]] [[gnu::const]] constexpr auto get_arrive_time(const Packet &pkt) -> double /// 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). { - return pkt_ptr->escape_time - (dot(pkt_ptr->pos, pkt_ptr->dir) / CLIGHT_PROP); + return pkt.escape_time - (dot(pkt.pos, pkt.dir) / CLIGHT_PROP); } -inline auto get_arrive_time_cmf(const struct packet *pkt_ptr) -> double { - return pkt_ptr->escape_time * std::sqrt(1. - (globals::vmax * globals::vmax / CLIGHTSQUARED)); -} - -constexpr int get_escapedirectionbin(std::span dir_in, std::span syn_dir) { - constexpr double xhat[3] = {1.0, 0.0, 0.0}; +[[nodiscard]] [[gnu::const]] constexpr auto get_escapedirectionbin(const std::array dir_in, + const std::array syn_dir) -> int { + constexpr auto xhat = std::array{1.0, 0.0, 0.0}; // sometimes dir vectors aren't accurately normalised const double dirmag = vec_len(dir_in); - std::array dir = {dir_in[0] / dirmag, dir_in[1] / dirmag, dir_in[2] / dirmag}; + const auto dir = std::array{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); - std::array vec1 = {0}; - cross_prod(dir, syn_dir, vec1); + const auto vec1 = cross_prod(dir, syn_dir); - std::array vec2 = {0}; - cross_prod(xhat, syn_dir, vec2); + const auto vec2 = cross_prod(xhat, syn_dir); const double cosphi = dot(vec1, vec2) / vec_len(vec1) / vec_len(vec2); - std::array vec3 = {0}; - cross_prod(vec2, syn_dir, vec3); + const auto vec3 = cross_prod(vec2, syn_dir); const double testphi = dot(vec1, vec3); - int phibin = 0; - if (testphi >= 0) { - phibin = static_cast(acos(cosphi) / 2. / PI * NPHIBINS); - } else { - phibin = static_cast((acos(cosphi) + PI) / 2. / PI * NPHIBINS); - } + // with phi defined according to y = cos(theta) * sin(phi), the + // phibins are in decreasing phi order (i.e. the upper side of bin zero 0 is 2pi) + const int phibin = static_cast((testphi >= 0 ? acos(cosphi) : acos(cosphi) + PI) / 2. / PI * NPHIBINS); + + assert_testmodeonly(phibin >= 0); assert_testmodeonly(phibin < NPHIBINS); const int na = static_cast((costhetabin * NPHIBINS) + phibin); assert_always(na < MABINS); @@ -238,4 +214,187 @@ constexpr int get_escapedirectionbin(std::span dir_in, std::spa return na; } +[[nodiscard]] inline auto get_rand_isotropic_unitvec() -> std::array +// Assuming isotropic distribution, get a random direction vector +{ + const double costheta = -1 + (2. * rng_uniform()); + + const double phi = rng_uniform() * 2 * PI; + + const double sintheta = std::sqrt(1. - (costheta * costheta)); + + return std::array{sintheta * std::cos(phi), sintheta * std::sin(phi), costheta}; +} + +[[nodiscard]] [[gnu::const]] constexpr auto rot_angle(const std::array n1, const std::array n2, + const std::array ref1, + const std::array 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 + + // ref1_sc is the ref1 axis in the scattering plane ref1 = n1 x ( n1 x n2 ) + const double n1_dot_n2 = dot(n1, n2); + auto ref1_sc = std::array{n1[0] * n1_dot_n2 - n2[0], n1[1] * n1_dot_n2 - n2[1], n1[2] * n1_dot_n2 - n2[2]}; + ref1_sc = vec_norm(ref1_sc); + + const double cos_stokes_rot_1 = std::clamp(dot(ref1_sc, ref1), -1., 1.); + const double cos_stokes_rot_2 = dot(ref1_sc, ref2); + + double i = 0; + if ((cos_stokes_rot_1 > 0) && (cos_stokes_rot_2 > 0)) { + i = acos(cos_stokes_rot_1); + } else if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 > 0)) { + i = PI - acos(fabs(cos_stokes_rot_1)); + } else if ((cos_stokes_rot_1 > 0) && (cos_stokes_rot_2 < 0)) { + i = 2 * PI - acos(cos_stokes_rot_1); + } else if ((cos_stokes_rot_1 < 0) && (cos_stokes_rot_2 < 0)) { + i = PI + acos(fabs(cos_stokes_rot_1)); + } + if (cos_stokes_rot_1 == 0) { + i = PI / 2.; + } + if (cos_stokes_rot_2 == 0) { + i = 0.; + } + + return i; +} + +// Routine to compute the meridian frame axes ref1 and ref2 +[[nodiscard]] [[gnu::const]] constexpr auto meridian(const std::array n) + -> std::tuple, std::array> { + // for ref_1 use (from triple product rule) + const double n_xylen = std::sqrt(n[0] * n[0] + n[1] * n[1]); + const auto ref1 = + std::array{-1. * n[0] * n[2] / n_xylen, -1. * n[1] * n[2] / n_xylen, (1 - (n[2] * n[2])) / n_xylen}; + + // for ref_2 use vector product of n_cmf with ref1 + const auto ref2 = cross_prod(ref1, n); + return {ref1, ref2}; +} + +[[nodiscard]] [[gnu::const]] constexpr auto lorentz(const std::array e_rf, const std::array n_rf, + const std::array v) -> std::array { + // Use Lorentz transformations to get e_cmf from e_rf + + const auto beta = std::array{v[0] / CLIGHT, v[1] / CLIGHT, v[2] / CLIGHT}; + const double vsqr = dot(beta, beta); + + const double gamma_rel = 1. / (sqrt(1 - vsqr)); + + const 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)}; + + const std::array e_perp{e_rf[0] - e_par[0], e_rf[1] - e_par[1], e_rf[2] - e_par[2]}; + + const auto b_rf = cross_prod(n_rf, e_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]}; + + const auto v_cr_b = cross_prod(beta, b_rf); + + // 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]}; + + auto e_cmf = std::array{e_par[0] + gamma_rel * (e_perp[0] + v_cr_b[0]), + e_par[1] + gamma_rel * (e_perp[1] + v_cr_b[1]), + e_par[2] + gamma_rel * (e_perp[2] + v_cr_b[2])}; + return vec_norm(e_cmf); +} + +// Routine to transform the Stokes Parameters from RF to CMF +constexpr auto frame_transform(const std::array n_rf, double *Q, double *U, + const std::array v) -> std::array { + // Meridian frame in the RF + const auto [ref1_rf, ref2_rf] = meridian(n_rf); + + const double Q0 = *Q; + const double U0 = *U; + + // Compute polarisation (which is invariant) + const double p = sqrt(Q0 * Q0 + U0 * U0); + + // We want to compute the angle between ref1 and the electric field + double rot_angle = 0; + + if (p > 0) { + 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.; + } else if ((cos2rot_angle < 0) && (sin2rot_angle > 0)) { + rot_angle = (PI - acos(fabs(cos2rot_angle))) / 2.; + } else if ((cos2rot_angle < 0) && (sin2rot_angle < 0)) { + rot_angle = (PI + acos(fabs(cos2rot_angle))) / 2.; + } else if ((cos2rot_angle > 0) && (sin2rot_angle < 0)) { + rot_angle = (2. * PI - acos(fabs(cos2rot_angle))) / 2.; + } else if (cos2rot_angle == 0) { + rot_angle = 0.25 * PI; + if (U0 < 0) { + rot_angle = 0.75 * PI; + } + } + if (sin2rot_angle == 0) { + rot_angle = 0.; + if (Q0 < 0) { + rot_angle = 0.5 * PI; + } + } + } + + // Define electric field by linear combination of ref1 and ref2 (using the angle just computed) + + const auto elec_rf = std::array{cos(rot_angle) * ref1_rf[0] - sin(rot_angle) * ref2_rf[0], + cos(rot_angle) * ref1_rf[1] - sin(rot_angle) * ref2_rf[1], + cos(rot_angle) * ref1_rf[2] - sin(rot_angle) * ref2_rf[2]}; + + // Aberration + const auto n_cmf = angle_ab(n_rf, v); + + // Lorentz transformation of E + const auto elec_cmf = lorentz(elec_rf, n_rf, v); + + // Meridian frame in the CMF + const auto [ref1_cmf, ref2_cmf] = meridian(n_cmf); + + // Projection of E onto ref1 and ref2 + const double cosine_elec_ref1 = dot(elec_cmf, ref1_cmf); + const double cosine_elec_ref2 = dot(elec_cmf, ref2_cmf); + + // Compute the angle between ref1 and the electric field + 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 = PI + acos(fabs(cosine_elec_ref1)); + } else if ((cosine_elec_ref1 < 0) && (cosine_elec_ref2 < 0)) { + theta_rot = PI - acos(fabs(cosine_elec_ref1)); + } else if ((cosine_elec_ref1 > 0) && (cosine_elec_ref2 > 0)) { + theta_rot = 2 * PI - acos(cosine_elec_ref1); + } + if (cosine_elec_ref1 == 0) { + theta_rot = PI / 2.; + } + if (cosine_elec_ref2 == 0) { + theta_rot = 0.; + } + if (cosine_elec_ref1 > 1) { + theta_rot = 0.; + } + if (cosine_elec_ref1 < -1) { + theta_rot = PI; + } + + // Compute Stokes Parameters in the CMF + *Q = cos(2 * theta_rot) * p; + *U = sin(2 * theta_rot) * p; + + return n_cmf; +} + #endif // VECTORS_H \ No newline at end of file diff --git a/vpkt.cc b/vpkt.cc index fedde4168..5bddf2761 100644 --- a/vpkt.cc +++ b/vpkt.cc @@ -1,34 +1,45 @@ #include "vpkt.h" +#include #include #include +#include #include #include -#include - +#include +#include +#include +#include +#include +#include + +#include "artisoptions.h" #include "atomic.h" +#include "constants.h" +#include "globals.h" #include "grid.h" #include "ltepop.h" +#include "packet.h" #include "rpkt.h" #include "sn3d.h" -#include "stats.h" -#include "update_grid.h" #include "vectors.h" +namespace { + struct stokeparams { double i = 0.; double q = 0.; double u = 0.; }; -struct vspecpol { - struct stokeparams flux[VMNUBINS]; - float lower_time = NAN; - float delta_t = NAN; +struct VSpecPol { + stokeparams flux[VMNUBINS]; + float lower_time{NAN}; + float delta_t{NAN}; }; -struct vspecpol **vspecpol = nullptr; +VSpecPol **vspecpol{}; float lower_freq_vspec[VMNUBINS]; float delta_freq_vspec[VMNUBINS]; @@ -45,20 +56,22 @@ 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; -std::vector exclude; // vector of opacity contribution setups - //-1: no line opacity; -2: no bf opacity; -3: no ff opacity; -4: no es opacity, +std::vector exclude; // vector of opacity contribution setups: + // 0: full opacity + // -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 ----------- +std::ofstream vpkt_contrib_file; + +// --------- VPacket GRID ----------- struct vgrid { std::vector> flux; - double yvel = NAN; - double zvel = NAN; + double yvel{NAN}; + double zvel{NAN}; }; struct vgrid vgrid_i[VGRID_NY][VGRID_NZ]; @@ -72,51 +85,41 @@ std::vector nu_grid_min; std::vector nu_grid_max; bool vgrid_on; -double dlogt_vspec = NAN; -double dlognu_vspec = NAN; - -// number of virtual packets in a given timestep -int nvpkt; - -// number of escaped virtual packet in a given timestep (with tau < tau_max) -int nvpkt_esc1; // electron scattering event -int nvpkt_esc2; // kpkt deactivation -int nvpkt_esc3; // macroatom deactivation +double dlogt_vspec{NAN}; +double dlognu_vspec{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 { +constexpr 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) { +void add_to_vspecpol(const Packet &vpkt, const int obsdirindex, const int opachoiceindex, 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; + const int nt = static_cast((log(t_arrive) - log(VSPEC_TIMEMIN)) / dlogt_vspec); + const int nnu = static_cast((log(vpkt.nu_rf) - log(VSPEC_NUMIN)) / dlognu_vspec); + if (nt < 0 || nt >= VMTBINS || nnu < 0 || nnu >= VMNUBINS) { + return; + } - /// 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; + const int ind_comb = Nspectra * obsdirindex + opachoiceindex; + 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); - } - } + atomicadd(vspecpol[nt][ind_comb].flux[nnu].i, vpkt.stokes[0] * pktcontrib); + atomicadd(vspecpol[nt][ind_comb].flux[nnu].q, vpkt.stokes[1] * pktcontrib); + atomicadd(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; +void add_to_vpkt_grid(const Packet &vpkt, const std::array vel, const int wlbin, const int obsdirindex, + const std::array obs) { + double vref1{NAN}; + double vref2{NAN}; // obs is the observer orientation @@ -155,70 +158,58 @@ static void add_to_vpkt_grid(const struct packet &vpkt, std::span 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); + atomicadd(vgrid_i[ny][nz].flux[wlbin][obsdirindex], vpkt.stokes[0] * vpkt.e_rf); + atomicadd(vgrid_q[ny][nz].flux[wlbin][obsdirindex], vpkt.stokes[1] * vpkt.e_rf); + atomicadd(vgrid_u[ny][nz].flux[wlbin][obsdirindex], 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; +auto rlc_emiss_vpkt(const Packet &pkt, const double t_current, const double t_arrive, const double nu_rf, + const double e_rf, const int obsdirindex, const std::array obsdir, + const enum packet_type type_before_rpkt, std::stringstream &vpkt_contrib_row) -> bool { int mgi = 0; - struct packet vpkt = *pkt_ptr; + Packet vpkt = pkt; + vpkt.nu_rf = nu_rf; + vpkt.e_rf = e_rf; + vpkt.dir = obsdir; + vpkt.last_cross = BOUNDARY_NONE; bool end_packet = false; double ldist = 0; double t_future = t_current; - for (int ind = 0; ind < Nspectra; ind++) { - tau_vpkt[ind] = 0; + for (int opacindex = 0; opacindex < Nspectra; opacindex++) { + tau_vpkt[opacindex] = 0; } - 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 - 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; + atomicadd(nvpkt, 1); // increment the number of virtual packet in the given timestep + const auto vel_vec = get_velocity(pkt.pos, t_current); double Qi = vpkt.stokes[1]; double Ui = vpkt.stokes[2]; // ------------ SCATTERING EVENT: dipole function -------------------- - 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) { + double pn{NAN}; + double I{NAN}; + double Q{NAN}; + double U{NAN}; + if (type_before_rpkt == 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); + auto old_dir_cmf = frame_transform(pkt.dir, &Qi, &Ui, vel_vec); // Need to rotate Stokes Parameters in the scattering plane - double obs_cmf[3]; - angle_ab(vpkt.dir, vel_vec, obs_cmf); + auto obs_cmf = angle_ab(vpkt.dir, vel_vec); - meridian(old_dir_cmf, ref1, ref2); + auto [ref1_old, ref2_old] = meridian(old_dir_cmf); // 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. - const double i1 = rot_angle(old_dir_cmf, obs_cmf, ref1, ref2); + const double i1 = rot_angle(old_dir_cmf, obs_cmf, ref1_old, ref2_old); const double cos2i1 = cos(2 * i1); const double sin2i1 = sin(2 * i1); @@ -241,7 +232,7 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu // Need to rotate Stokes Parameters out of the scattering plane to the meridian frame - meridian(obs_cmf, ref1, ref2); + auto [ref1, ref2] = meridian(obs_cmf); // 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 @@ -255,11 +246,11 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu // Transform Stokes Parameters from the CMF to the RF - const double vel_rev[3] = {-vel_vec[0], -vel_vec[1], -vel_vec[2]}; + const std::array vel_rev{-vel_vec[0], -vel_vec[1], -vel_vec[2]}; - frame_transform(obs_cmf, &Q, &U, vel_rev, obsdir); + frame_transform(obs_cmf, &Q, &U, vel_rev); - } else if (realtype == TYPE_KPKT || realtype == TYPE_MA) { + } else if (type_before_rpkt == TYPE_KPKT || type_before_rpkt == TYPE_MA) { // MACROATOM and KPKT: isotropic emission I = 1; Q = 0; @@ -270,18 +261,18 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu // compute the optical depth to boundary mgi = grid::get_cell_modelgridindex(vpkt.where); - struct rpkt_continuum_absorptioncoeffs chi_vpkt_cont = {}; + thread_local static struct Rpkt_continuum_absorptioncoeffs chi_vpkt_cont {}; while (!end_packet) { // distance to the next cell - const double sdist = - grid::boundary_distance(vpkt.dir, vpkt.pos, vpkt.prop_time, vpkt.where, &snext, &vpkt.last_cross); + const auto [sdist, snext] = + grid::boundary_distance(vpkt.dir, vpkt.pos, vpkt.prop_time, vpkt.where, &vpkt.last_cross); const double s_cont = sdist * t_current * t_current * t_current / (t_future * t_future * t_future); if (mgi == grid::get_npts_model()) { vpkt.next_trans = -1; } else { - calculate_chi_rpkt_cont(vpkt.nu_cmf, &chi_vpkt_cont, mgi, false); + calculate_chi_rpkt_cont(vpkt.nu_cmf, chi_vpkt_cont, nullptr, mgi); const double chi_cont = chi_vpkt_cont.total; @@ -290,10 +281,10 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu 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; + const double chi_cont_noff = chi_cont - chi_vpkt_cont.ffheat; tau_vpkt[ind] += chi_cont_noff * s_cont; } else if (exclude[ind] == -4) { - const double chi_cont_noes = chi_cont - chi_vpkt_cont.es; + const double chi_cont_noes = chi_cont - chi_vpkt_cont.ffescat; tau_vpkt[ind] += chi_cont_noes * s_cont; } else { tau_vpkt[ind] += chi_cont * s_cont; @@ -302,11 +293,11 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu // kill vpkt with high optical depth if (all_taus_past_taumax(tau_vpkt, tau_max_vpkt)) { - return; + return false; } - struct packet dummypkt_abort = vpkt; - move_pkt_withtime(&dummypkt_abort, sdist); + 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; @@ -316,95 +307,90 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu const int lineindex = closest_transition(vpkt.nu_cmf, vpkt.next_trans); if (lineindex < 0) { + // no more lines below the current frequency vpkt.next_trans = globals::nlines + 1; - } else { - const double nutrans = globals::linelist[lineindex].nu; + break; + } + const double nutrans = globals::linelist[lineindex].nu; - vpkt.next_trans = lineindex + 1; + vpkt.next_trans = lineindex + 1; - ldist = get_linedistance(t_current, vpkt.nu_cmf, nutrans, d_nu_on_d_l); + ldist = get_linedistance(vpkt.prop_time, vpkt.nu_cmf, nutrans, d_nu_on_d_l); - 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 + 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; - // printout("ldist > sdist : line in the next cell\n"); - break; - } + vpkt.next_trans -= 1; + // printout("ldist > sdist : line in the next cell\n"); + break; + } - 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 = std::max(0., (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; - } - } + const double t_line = vpkt.prop_time + 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; - // kill vpkt with high optical depth - if (all_taus_past_taumax(tau_vpkt, tau_max_vpkt)) { - return; + // Check on the element to exclude (or -1 for no line opacity) + const int anumber = get_atomicnumber(element); + for (int ind = 0; ind < Nspectra; ind++) { + if (exclude[ind] != -1 && (exclude[ind] != anumber)) { + tau_vpkt[ind] += tau_line; } } + + // kill vpkt with high optical depth + if (all_taus_past_taumax(tau_vpkt, tau_max_vpkt)) { + return false; + } } } // virtual packet is still at the starting position // move it to cell boundary and go to next cell - // printf("I'm changing cell. I'm going from nu_cmf = %.e ",dummy_ptr->nu_cmf); t_future += (sdist / CLIGHT_PROP); + move_pkt_withtime(vpkt, sdist); vpkt.prop_time = t_future; - move_pkt(&vpkt, sdist); - grid::change_cell(&vpkt, snext); + grid::change_cell(vpkt, snext); end_packet = (vpkt.type == TYPE_ESCAPE); 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 != 0) { - return; + return false; } } // increment the number of escaped virtual packet in the given timestep - if (realtype == TYPE_RPKT) { - safeincrement(nvpkt_esc1); - } else if (realtype == TYPE_KPKT) { - safeincrement(nvpkt_esc2); - } else if (realtype == TYPE_MA) { - safeincrement(nvpkt_esc3); + if (type_before_rpkt == TYPE_RPKT) { + atomicadd(nvpkt_esc1, 1); + } else if (type_before_rpkt == TYPE_KPKT) { + atomicadd(nvpkt_esc2, 1); + } else if (type_before_rpkt == TYPE_MA) { + atomicadd(nvpkt_esc3, 1); } - const double t_arrive = t_current - (dot(pkt_ptr->pos, vpkt.dir) / CLIGHT_PROP); // -------------- final stokes vector --------------- + if (VPKT_WRITE_CONTRIBS) { + vpkt_contrib_row << " " << t_arrive / DAY << " " << vpkt.nu_rf; + } + for (int ind = 0; ind < Nspectra; ind++) { - // printout("obsbin %d spectrum %d tau_vpkt %g\n", obsbin, ind, tau_vpkt[ind]); - const double prob = pn * exp(-tau_vpkt[ind]); + const double prob = pn * std::exp(-tau_vpkt[ind]); assert_always(std::isfinite(prob)); @@ -416,9 +402,11 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu assert_always(std::isfinite(stokeval)); } - // bin on fly and produce file with spectrum + add_to_vspecpol(vpkt, obsdirindex, ind, t_arrive); - add_to_vspecpol(vpkt, obsbin, ind, t_arrive); + if constexpr (VPKT_WRITE_CONTRIBS) { + vpkt_contrib_row << " " << vpkt.e_rf * prob; + } } // vpkt grid @@ -433,19 +421,20 @@ static void rlc_emiss_vpkt(const struct packet *const pkt_ptr, const double t_cu 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); + add_to_vpkt_grid(vpkt, vel_vec, wlbin, obsdirindex, obsdir); } } } } + return true; // true if we added columns to vpkt_contrib_row } -static void init_vspecpol() { - vspecpol = static_cast(malloc(VMTBINS * sizeof(struct vspecpol *))); +void init_vspecpol() { + vspecpol = static_cast(malloc(VMTBINS * sizeof(VSpecPol *))); const int indexmax = Nspectra * Nobs; for (int p = 0; p < VMTBINS; p++) { - vspecpol[p] = static_cast(malloc(indexmax * sizeof(struct vspecpol))); + vspecpol[p] = static_cast(malloc(indexmax * sizeof(VSpecPol))); } dlogt_vspec = (log(VSPEC_TIMEMAX) - log(VSPEC_TIMEMIN)) / VMTBINS; @@ -466,15 +455,15 @@ static void init_vspecpol() { 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; + flux.i = 0.; + flux.q = 0.; + flux.u = 0.; } } } } -static void write_vspecpol(FILE *specpol_file) { +void write_vspecpol(FILE *specpol_file) { for (int ind_comb = 0; ind_comb < (Nobs * Nspectra); ind_comb++) { fprintf(specpol_file, "%g ", 0.); @@ -509,7 +498,7 @@ static void write_vspecpol(FILE *specpol_file) { } } -static void read_vspecpol(int my_rank, int nts) { +void read_vspecpol(int my_rank, int nts) { char filename[MAXFILENAMELENGTH]; snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_ts%d.tmp", 0, my_rank, nts); @@ -517,9 +506,9 @@ static void read_vspecpol(int my_rank, int nts) { FILE *vspecpol_file = fopen_required(filename, "r"); - float a = NAN; - float b = NAN; - float c = NAN; + float a{NAN}; + float b{NAN}; + float c{NAN}; for (int ind_comb = 0; ind_comb < (Nobs * Nspectra); ind_comb++) { // Initialise I,Q,U fluxes from temporary files @@ -558,7 +547,7 @@ static void read_vspecpol(int my_rank, int nts) { fclose(vspecpol_file); } -static void init_vpkt_grid() { +void init_vpkt_grid() { const double ybin = 2 * globals::vmax / VGRID_NY; const double zbin = 2 * globals::vmax / VGRID_NZ; @@ -588,17 +577,17 @@ static void init_vpkt_grid() { } } -static void write_vpkt_grid(FILE *vpkt_grid_file) { - for (int obsbin = 0; obsbin < Nobs; obsbin++) { +void write_vpkt_grid(FILE *vpkt_grid_file) { + for (int obsdirindex = 0; obsdirindex < Nobs; obsdirindex++) { 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[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, "%g ", vgrid_i[n][m].flux[wlbin][obsdirindex]); + fprintf(vpkt_grid_file, "%g ", vgrid_q[n][m].flux[wlbin][obsdirindex]); + fprintf(vpkt_grid_file, "%g ", vgrid_u[n][m].flux[wlbin][obsdirindex]); fprintf(vpkt_grid_file, "\n"); } @@ -607,7 +596,7 @@ static void write_vpkt_grid(FILE *vpkt_grid_file) { } } -static void read_vpkt_grid(const int my_rank, const int nts) { +void read_vpkt_grid(const int my_rank, const int nts) { if (!vgrid_on) { return; } @@ -617,16 +606,16 @@ static void read_vpkt_grid(const int my_rank, const int 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 obsdirindex = 0; obsdirindex < Nobs; obsdirindex++) { 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[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, "%lg ", &vgrid_i[n][m].flux[wlbin][obsdirindex]) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_q[n][m].flux[wlbin][obsdirindex]) == 1); + assert_always(fscanf(vpkt_grid_file, "%lg ", &vgrid_u[n][m].flux[wlbin][obsdirindex]) == 1); assert_always(fscanf(vpkt_grid_file, "\n") == 0); } @@ -637,6 +626,8 @@ static void read_vpkt_grid(const int my_rank, const int nts) { fclose(vpkt_grid_file); } +} // anonymous namespace + 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); @@ -644,7 +635,7 @@ void vpkt_remove_temp_file(const int nts, const int my_rank) { for (auto &filename : filenames) { if (access(filename, F_OK) == 0) { - remove(filename); + std::remove(filename); printout("Deleted %s\n", filename); } } @@ -665,7 +656,7 @@ void read_parameterfile_vpkt() { if (fabs(nz_obs_vpkt[i]) > 1) { printout("Wrong observer direction\n"); - abort(); + std::abort(); } else if (nz_obs_vpkt[i] == 1) { nz_obs_vpkt[i] = 0.9999; } else if (nz_obs_vpkt[i] == -1) { @@ -733,6 +724,8 @@ void read_parameterfile_vpkt() { assert_always(VSPEC_TIMEMIN_input >= VSPEC_TIMEMIN); assert_always(VSPEC_TIMEMAX_input <= VSPEC_TIMEMAX); + assert_always(VSPEC_TIMEMIN_input >= globals::tmin); + assert_always(VSPEC_TIMEMAX_input <= globals::tmax); // frequency window. dum4 restrict vpkt to a frequency range, dum5 indicates the number of ranges, // followed by a list of ranges (dum6,dum7) @@ -758,6 +751,9 @@ void read_parameterfile_vpkt() { VSPEC_NUMIN_input[i] = CLIGHT / (lmax_vspec_input * 1e-8); VSPEC_NUMAX_input[i] = CLIGHT / (lmin_vspec_input * 1e-8); + + assert_always(VSPEC_NUMIN_input[i] >= VSPEC_NUMIN); + assert_always(VSPEC_NUMAX_input[i] <= VSPEC_NUMAX); } } else { Nrange = 1; @@ -796,8 +792,8 @@ void read_parameterfile_vpkt() { printout("vpkt.txt: velocity grid map %s\n", (vgrid_on) ? "ENABLED" : "DISABLED"); if (vgrid_on) { - double tmin_grid_in_days = NAN; - double tmax_grid_in_days = NAN; + double tmin_grid_in_days{NAN}; + double tmax_grid_in_days{NAN}; // Specify time range for velocity grid map assert_always(fscanf(input_file, "%lg %lg \n", &tmin_grid_in_days, &tmax_grid_in_days) == 2); tmin_grid = tmin_grid_in_days * DAY; @@ -828,16 +824,16 @@ void read_parameterfile_vpkt() { fclose(input_file); } -void vpkt_write_timestep(const int nts, const int my_rank, const int tid, - const bool is_final) { // write specpol of the virtual packets +void vpkt_write_timestep(const int nts, const int my_rank, const bool is_final) { if constexpr (!VPKT_ON) { return; } + // write specpol of the virtual packets char filename[MAXFILENAMELENGTH]; if (is_final) { - snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d-%d.out", my_rank, tid); + snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d-%d.out", my_rank, 0); } else { snprintf(filename, MAXFILENAMELENGTH, "vspecpol_%d_%d_ts%d.tmp", 0, my_rank, nts); } @@ -849,7 +845,7 @@ void vpkt_write_timestep(const int nts, const int my_rank, const int tid, if (vgrid_on) { if (is_final) { - snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d-%d.out", my_rank, tid); + snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d-%d.out", my_rank, 0); } else { snprintf(filename, MAXFILENAMELENGTH, "vpkt_grid_%d_%d_ts%d.tmp", 0, my_rank, nts); } @@ -861,7 +857,7 @@ void vpkt_write_timestep(const int nts, const int my_rank, const int tid, } } -void vpkt_init(const int nts, const int my_rank, const int tid, const bool continued_from_saved) { +void vpkt_init(const int nts, const int my_rank, const bool continued_from_saved) { if constexpr (!VPKT_ON) { return; } @@ -871,6 +867,29 @@ void vpkt_init(const int nts, const int my_rank, const int tid, const bool conti init_vpkt_grid(); } + if constexpr (VPKT_WRITE_CONTRIBS) { + char filename[MAXFILENAMELENGTH]; + snprintf(filename, MAXFILENAMELENGTH, "vpackets_%.4d.out", my_rank); + if (std::filesystem::exists(filename) && !continued_from_saved) { + std::remove(filename); + } + vpkt_contrib_file = std::ofstream(filename, std::ios::app); + + if (!continued_from_saved) { + vpkt_contrib_file << "#emissiontype trueemissiontype absorption_type absorption_freq"; + + for (int obsdirindex = 0; obsdirindex < Nobs; obsdirindex++) { + vpkt_contrib_file << " dir" << obsdirindex << "_t_arrive_d dir" << obsdirindex << "_nu_rf"; + for (int ind = 0; ind < Nspectra; ind++) { + vpkt_contrib_file << " dir" << obsdirindex << "_e_rf_" << ind; + } + } + + vpkt_contrib_file << "\n"; + vpkt_contrib_file.flush(); + } + } + if (continued_from_saved) { // Continue simulation: read into temporary files @@ -882,242 +901,69 @@ void vpkt_init(const int nts, const int my_rank, const int tid, const bool conti } } -auto vpkt_call_estimators(struct packet *pkt_ptr, const enum packet_type realtype) -> void { +auto vpkt_call_estimators(Packet &pkt, const enum packet_type type_before_rpkt) -> void { if constexpr (!VPKT_ON) { return; } // Cut on vpkts - const int mgi = grid::get_cell_modelgridindex(pkt_ptr->where); + const int mgi = grid::get_cell_modelgridindex(pkt.where); if (grid::modelgrid[mgi].thick != 0) { return; } - const double t_current = pkt_ptr->prop_time; + const double t_current = pkt.prop_time; - double vel_vec[3]; - get_velocity(pkt_ptr->pos, vel_vec, pkt_ptr->prop_time); + const auto vel_vec = get_velocity(pkt.pos, pkt.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) { - pkt_ptr->next_trans = lineindex + 1; - } - } + std::stringstream vpkt_contrib_row; - for (int obsbin = 0; obsbin < Nobs; obsbin++) { + bool any_dir_escaped = false; + for (int obsdirindex = 0; obsdirindex < Nobs; obsdirindex++) { // loop over different observer directions - 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 std::array obsdir{ + sqrt(1 - nz_obs_vpkt[obsdirindex] * nz_obs_vpkt[obsdirindex]) * cos(phiobs[obsdirindex]), + sqrt(1 - nz_obs_vpkt[obsdirindex] * nz_obs_vpkt[obsdirindex]) * sin(phiobs[obsdirindex]), + nz_obs_vpkt[obsdirindex]}; - const double t_arrive = t_current - (dot(pkt_ptr->pos, obsdir) / CLIGHT_PROP); + const double t_arrive = t_current - (dot(pkt.pos, obsdir) / CLIGHT_PROP); + bool dir_escaped = false; 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); + const double doppler = doppler_nucmf_on_nurf(obsdir, vel_vec); + const double nu_rf = pkt.nu_cmf / doppler; + const double e_rf = pkt.e_cmf / doppler; for (int i = 0; i < Nrange; i++) { // Loop over frequency ranges - if (nu_rf > VSPEC_NUMIN_input[i] && nu_rf < VSPEC_NUMAX_input[i]) { + if ((nu_rf > VSPEC_NUMIN_input[i] && nu_rf < VSPEC_NUMAX_input[i]) || + (pkt.absorptionfreq > VSPEC_NUMIN_input[i] && pkt.absorptionfreq < VSPEC_NUMAX_input[i])) { // frequency selection - - rlc_emiss_vpkt(pkt_ptr, t_current, obsbin, obsdir, realtype); + dir_escaped = rlc_emiss_vpkt(pkt, t_current, t_arrive, nu_rf, e_rf, obsdirindex, obsdir, type_before_rpkt, + vpkt_contrib_row); + break; // assume that the frequency ranges do not overlap } } } - } -} -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 - - // ref1_sc is the ref1 axis in the scattering plane ref1 = n1 x ( n1 x n2 ) - 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); - const double cos_stokes_rot_2 = dot(ref1_sc, ref2); - - if (cos_stokes_rot_1 < -1) { - cos_stokes_rot_1 = -1; - } - if (cos_stokes_rot_1 > 1) { - 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); - } 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 = M_PI / 2.; - } - if (cos_stokes_rot_2 == 0) { - i = 0.0; - } - - if (!std::isfinite(i)) { - printout("Warning NaN: %3.6f \t %3.6f \t %3.6f \n", cos_stokes_rot_1, cos_stokes_rot_2, acos(cos_stokes_rot_1)); - } - - return i; -} - -// Routine to compute the meridian frame axes ref1 and ref2 -void meridian(std::span n, std::span ref1, std::span ref2) { - // for ref_1 use (from triple product rule) - 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]}; - - 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(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); - - const double Q0 = *Q; - const double U0 = *U; - - // Compute polarisation (which is invariant) - const double p = sqrt(Q0 * Q0 + U0 * U0); - - // We want to compute the angle between ref1 and the electric field - double rot_angle = 0; - - if (p > 0) { - 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.; - } 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 * M_PI; - } - } - if (sin2rot_angle == 0) { - rot_angle = 0.0; - if (Q0 < 0) { - rot_angle = 0.5 * M_PI; + if (dir_escaped) { + any_dir_escaped = true; + } else { + vpkt_contrib_row << " -1. -1."; // t_arrive_d nu_rf + for (int ind = 0; ind < Nspectra; ind++) { + vpkt_contrib_row << " 0."; // e_rf_diri_j } } } - - // Define electric field by linear combination of ref1 and ref2 (using the angle just computed) - - 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(elec_rf, n_rf, v, elec_cmf); - - // Meridian frame in the CMF - meridian(n_cmf, ref1, ref2); - - // Projection of E onto ref1 and ref2 - 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 - 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 (VPKT_WRITE_CONTRIBS && any_dir_escaped) { + vpkt_contrib_file << pkt.emissiontype << " " << pkt.trueemissiontype << " " << pkt.absorptiontype << " " + << pkt.absorptionfreq; + vpkt_contrib_file << vpkt_contrib_row.rdbuf() << "\n"; + vpkt_contrib_file.flush(); } - if (cosine_elec_ref1 == 0) { - theta_rot = M_PI / 2.; - } - if (cosine_elec_ref2 == 0) { - theta_rot = 0.0; - } - if (cosine_elec_ref1 > 1) { - theta_rot = 0.0; - } - 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; } diff --git a/vpkt.h b/vpkt.h index 95db78daa..2d6c7ca0c 100644 --- a/vpkt.h +++ b/vpkt.h @@ -1,21 +1,18 @@ +#pragma once +#include #ifndef VPKT_H #define VPKT_H +#include #include -#include "artisoptions.h" +#include "constants.h" #include "packet.h" -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 read_parameterfile_vpkt(); -void vpkt_init(int nts, int my_rank, int tid, bool continued_from_saved); -void vpkt_call_estimators(struct packet *pkt_ptr, enum packet_type); -void vpkt_write_timestep(int nts, int my_rank, int tid, bool is_final); +void vpkt_init(int nts, int my_rank, bool continued_from_saved); +void vpkt_call_estimators(Packet &pkt, enum packet_type type_before_rpkt); +void vpkt_write_timestep(int nts, int my_rank, bool is_final); void vpkt_remove_temp_file(int nts, int my_rank); @@ -34,11 +31,12 @@ 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 -extern int nvpkt_esc2; // kpkt deactivation -extern int nvpkt_esc3; // macroatom deactivation +// number of virtual packets in a given timestep +inline int nvpkt{0}; +inline int nvpkt_esc1{0}; // electron scattering event +inline int nvpkt_esc2{0}; // kpkt deactivation +inline int nvpkt_esc3{0}; // macroatom deactivation -extern double cell_is_optically_thick_vpkt; +inline double cell_is_optically_thick_vpkt; #endif // VPKT_H