├── .gitignore ├── utils └── larnest │ ├── data │ ├── lar_data.npz │ ├── nr_total_yield.csv │ ├── nr_charge_yield_alt.csv │ ├── alpha_light_yield.csv │ ├── alpha_charge_yield.csv │ └── nr_light_yield.csv │ ├── plot_benchmarks.py │ ├── parameters.py │ └── utils.py ├── src ├── TestLArSpectra.cpp ├── VDetector.cpp ├── ValidityTests.cpp ├── LArDetector.cpp ├── GammaHandler.cpp └── RandomGen.cpp ├── format.sh ├── include ├── NEST │ ├── ValidityTests.hh │ ├── GammaHandler.hh │ ├── TestLArSpectra.hh │ ├── RandomGen.hh │ ├── TestSpectra.hh │ ├── analysis_G2.hh │ ├── analysis.hh │ ├── execNEST.hh │ └── LArParameters.hh └── Detectors │ ├── LArDetector.hh │ ├── DS.hh │ ├── Detector_G3.hh │ ├── Xed_CWRU_Dahl.hh │ ├── DetectorExample_XENON10.hh │ ├── VDetector.hh │ └── Z3_FSR_HA.hh ├── GarfieldppIntegration └── CMakeLists.txt ├── CITATION.md ├── cmake ├── gcem.cmake └── NESTConfig.cmake.in ├── examples ├── leakage.sh ├── LArNEST │ ├── CMakeLists.txt │ ├── LArNESTNeutronCapture.cpp │ ├── LArNESTMeanYieldsBenchmarks.cpp │ ├── LegacyLArNESTBenchmarks.cpp │ └── LArNESTFluctuationBenchmarks.cpp ├── tracker.sh ├── CMakeLists.txt ├── Xe10_ERBand_Luiz.txt ├── Xe10_NRBand_Luiz.txt ├── efficiency.sh ├── loopNEST.in ├── LUXRun03_betaEff_Simulated.txt ├── LUXRun03_CH3TBand_SpikeFull.txt ├── LUXRun03_DDBand_SpikeFull.txt ├── pulseShape.cpp ├── GitDiffDetPerf.dat └── multipleScatter.cpp ├── LICENSE ├── G4integration ├── NESTStackingAction.hh ├── CMakeLists.txt ├── NESTStackingAction.cpp └── NESTProc.hh ├── .travis.yml ├── .zenodo.json └── CMakeLists.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /fastNEST.exe 2 | nbproject/* 3 | **/.DS_Store 4 | build/ 5 | *.png 6 | *.pyc 7 | *__pycache__/ 8 | -------------------------------------------------------------------------------- /utils/larnest/data/lar_data.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NESTCollaboration/nest/HEAD/utils/larnest/data/lar_data.npz -------------------------------------------------------------------------------- /src/TestLArSpectra.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TestLArSpectra.cpp 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #include "TestLArSpectra.hh" 10 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | 2 | for VARIABLE in "C" "cc" "hh" "h" "cpp" 3 | do 4 | find . -name "*.${VARIABLE}" -print0 | while read -d $'\0' file 5 | do 6 | echo "${file}" 7 | clang-format -style="{BasedOnStyle: Google,SortIncludes: false}" -i ${file} 8 | done 9 | done 10 | -------------------------------------------------------------------------------- /include/NEST/ValidityTests.hh: -------------------------------------------------------------------------------- 1 | #ifndef VALIDITYTESTS_HH 2 | #define VALIDITYTESTS_HH 1 3 | 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | class ValidityTests { 10 | public: 11 | static bool nearlyEqual(double a, double b, double epsilon = 1e-9); 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /GarfieldppIntegration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(MIN_ROOT_VERSION 6.12) 3 | 4 | add_executable(GenerateGasTable GenerateGarfieldGasTableForLiquidNoble.cpp) 5 | target_link_libraries(GenerateGasTable PUBLIC NESTCore) 6 | set(GARFIELD GenerateGasTable) 7 | 8 | install( 9 | TARGETS ${GARFIELD} 10 | RUNTIME DESTINATION bin 11 | ) 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CITATION.md: -------------------------------------------------------------------------------- 1 | Please cite this code when using it in a publication. There are two things that you will have to cite: the original NEST paper and the DOI associated to this release. 2 | 3 | * NEST: a comprehensive model for scintillation yield in liquid xenon, http://iopscience.iop.org/article/10.1088/1748-0221/6/10/P10002/meta 4 | * Noble Element Simulation Technique, https://zenodo.org/badge/latestdoi/96344242 5 | -------------------------------------------------------------------------------- /cmake/gcem.cmake: -------------------------------------------------------------------------------- 1 | if(NOT TARGET gcem) 2 | include(FetchContent) 3 | 4 | FetchContent_Declare( 5 | gcem 6 | GIT_REPOSITORY https://github.com/kthohr/gcem 7 | GIT_TAG v1.13.1 8 | ) 9 | 10 | 11 | FetchContent_GetProperties(gcem) 12 | if(NOT gcem_POPULATED) 13 | FetchContent_Populate(gcem) 14 | add_subdirectory(${gcem_SOURCE_DIR} ${gcem_BINARY_DIR}) 15 | endif() 16 | endif() 17 | -------------------------------------------------------------------------------- /examples/leakage.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | for f in 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 125 150 175 200 225 250 275 300 325 350 375 400 425 450 475 500 525 550 600 700 800 900 1000 1200 1400 1600 1800 2000 2500 3000 3500 4000 4500 5000 5500 6000 5 | 6 | do 7 | 8 | ./execNEST 2e6 beta 0 100 $f -1 0 > NESTOutputER.new 9 | ./execNEST 1e6 D-D 0 74 $f 200,0,-1 0 > NESTOutputNR.new 10 | examples/rootNEST NESTOutputER.new NESTOutputNR.new 11 | 12 | done 13 | -------------------------------------------------------------------------------- /include/Detectors/LArDetector.hh: -------------------------------------------------------------------------------- 1 | /** 2 | * @file LArDetector.hh 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #pragma once 10 | 11 | #include "VDetector.hh" 12 | 13 | /** 14 | * @brief An example LAr TPC detector. 15 | * 16 | */ 17 | class LArDetector : public VDetector { 18 | public: 19 | LArDetector(); 20 | ~LArDetector() override = default; 21 | void Initialization() override; 22 | 23 | private: 24 | }; -------------------------------------------------------------------------------- /src/VDetector.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // VDetector.cpp 3 | // 4 | // Adapted from Quentin Riffard by Jacob Cutter, May 8, 2018 5 | 6 | // ********************************************************************* 7 | // THIS DEFAULT VIRTUAL DETECTOR SHOULD ONLY BE MODIFIED BY DEVELOPERS. 8 | // PLEASE DEFINE YOUR OWN DETECTOR (see DetectorExample_XENON10.hh). 9 | // ********************************************************************* 10 | 11 | #include "VDetector.hh" 12 | 13 | VDetector::VDetector() { Initialization(); } 14 | 15 | VDetector::~VDetector() = default; 16 | 17 | void VDetector::Initialization() {} 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache Software License 2.0 2 | 3 | Copyright (c) 2018, NEST Collaboration 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | -------------------------------------------------------------------------------- /cmake/NESTConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | get_filename_component(NEST_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 4 | include(CMakeFindDependencyMacro) 5 | 6 | list(APPEND CMAKE_MODULE_PATH ${NEST_CMAKE_DIR}) 7 | set(NEST_INCLUDE_DIRS 8 | "${PACKAGE_PREFIX_DIR}/include" 9 | "${PACKAGE_PREFIX_DIR}/include/NEST" 10 | "${PACKAGE_PREFIX_DIR}/include/NEST/G4" 11 | "${PACKAGE_PREFIX_DIR}/include/Detectors" 12 | ) 13 | 14 | # Internal gcem build 15 | if(NOT TARGET gcem) 16 | find_dependency(gcem PATHS "${NEST_CMAKE_DIR}/../gcem") 17 | endif() 18 | 19 | if(NOT TARGET NEST::Core) 20 | include("${NEST_CMAKE_DIR}/NESTTargets.cmake") 21 | endif() 22 | 23 | -------------------------------------------------------------------------------- /src/ValidityTests.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ValidityTests.hh" 3 | 4 | using namespace std; 5 | 6 | bool ValidityTests::nearlyEqual(double a, double b, double epsilon) { 7 | // Handles cases of equality statements for floats 8 | if (a == b) { // shortcut, handles infinities 9 | return true; 10 | } 11 | double absA = fabs(a); 12 | double absB = fabs(b); 13 | double diff = fabs(a - b); 14 | 15 | if (a == 0 || b == 0 || (absA + absB < DBL_MIN)) { 16 | // a or b is zero or both are extremely close to it 17 | // relative error is less meaningful here 18 | return diff < (epsilon * DBL_MIN); 19 | } // use relative error 20 | return diff / fmin((absA + absB), DBL_MAX) < epsilon; 21 | } 22 | -------------------------------------------------------------------------------- /examples/LArNEST/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # add_executable(bareLArNEST bareLArNEST.cpp) 2 | # target_link_libraries(bareLArNEST PUBLIC NEST::Core) 3 | 4 | add_executable(LegacyLArNESTBenchmarks LegacyLArNESTBenchmarks.cpp) 5 | target_link_libraries(LegacyLArNESTBenchmarks PUBLIC NESTCore) 6 | 7 | add_executable(LArNESTMeanYieldsBenchmarks LArNESTMeanYieldsBenchmarks.cpp) 8 | target_link_libraries(LArNESTMeanYieldsBenchmarks PUBLIC NESTCore) 9 | 10 | add_executable(LArNESTFluctuationBenchmarks LArNESTFluctuationBenchmarks.cpp) 11 | target_link_libraries(LArNESTFluctuationBenchmarks PUBLIC NESTCore) 12 | 13 | add_executable(LArNESTNeutronCapture LArNESTNeutronCapture.cpp) 14 | target_link_libraries(LArNESTNeutronCapture PUBLIC NESTCore) 15 | -------------------------------------------------------------------------------- /G4integration/NESTStackingAction.hh: -------------------------------------------------------------------------------- 1 | #ifndef NESTStackingAction_h 2 | #define NESTStackingAction_h 1 3 | 4 | #include "globals.hh" 5 | 6 | #include "G4ClassificationOfNewTrack.hh" 7 | #include "G4StackManager.hh" 8 | #include "G4Track.hh" 9 | #include "G4UserStackingAction.hh" 10 | 11 | /// Control which particles are tracked by G4 12 | 13 | class NESTStackingAction : public G4UserStackingAction { 14 | public: 15 | NESTStackingAction(); 16 | virtual ~NESTStackingAction(); 17 | static G4StackManager* savedManager; 18 | static NESTStackingAction* theStackingAction; 19 | 20 | bool isUrgentEmpty(); 21 | 22 | virtual G4ClassificationOfNewTrack ClassifyNewTrack(const G4Track*); 23 | virtual void NewStage(); 24 | 25 | private: 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # This file is responsible for running the below commands each commit 2 | # on the TravisCI platform. 3 | 4 | language: cpp 5 | 6 | # A matrix build means that the following combinations of configurations 7 | # are tested. 8 | matrix: 9 | include: 10 | - os: osx # Note: we pay extra for OSX for Travis 11 | compiler: clang 12 | - os: linux 13 | compiler: gcc 14 | - os: linux 15 | compiler: clang 16 | 17 | # Email notifications 18 | notifications: 19 | email: 20 | - matthew.szydagis@gmail.com 21 | 22 | # Commands to install 23 | install: 24 | - mkdir build 25 | - cd build 26 | - cmake --version 27 | - cmake -DCMAKE_INSTALL_PREFIX=${PWD} .. 28 | - make 29 | - make install 30 | 31 | # Test commands 32 | script: 33 | - ls 34 | - ./bin/execNEST 100 WIMP 100 1e-40 10 100,100,100 42 35 | -------------------------------------------------------------------------------- /G4integration/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(MIN_G4_VERSION 10.03) 2 | 3 | find_package(Geant4 ${MIN_G4_VERSION} REQUIRED CONFIG) 4 | set(NEST_G4_SOURCES 5 | ${CMAKE_CURRENT_SOURCE_DIR}/NESTProc.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/NESTStackingAction.cpp 7 | ) 8 | set(NEST_G4_HEADERS 9 | ${CMAKE_CURRENT_SOURCE_DIR}/NESTProc.hh 10 | ${CMAKE_CURRENT_SOURCE_DIR}/NESTStackingAction.hh 11 | ) 12 | 13 | add_library( 14 | NESTG4 15 | ${NEST_G4_SOURCES} 16 | ${NEST_G4_HEADERS} 17 | ) 18 | target_link_libraries(NESTG4 PUBLIC NESTCore ${Geant4_LIBRARIES}) 19 | # Geant4 does not seem to have targets yet (last version checked: 1) 20 | target_include_directories( 21 | NESTG4 PUBLIC 22 | ${Geant4_INCLUDE_DIR} 23 | $ 24 | $ 25 | ) 26 | 27 | INSTALL(FILES ${NEST_G4_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/NEST/G4) 28 | -------------------------------------------------------------------------------- /G4integration/NESTStackingAction.cpp: -------------------------------------------------------------------------------- 1 | #include "NESTStackingAction.hh" 2 | #include "G4OpticalPhoton.hh" 3 | #include 4 | 5 | NESTStackingAction::NESTStackingAction() {} 6 | NESTStackingAction::~NESTStackingAction() {} 7 | 8 | NESTStackingAction* NESTStackingAction::theStackingAction = 0; 9 | G4StackManager* NESTStackingAction::savedManager = 0; 10 | 11 | G4ClassificationOfNewTrack NESTStackingAction::ClassifyNewTrack( 12 | const G4Track* track) { 13 | savedManager = stackManager; 14 | if (track->GetDefinition() == G4OpticalPhoton::OpticalPhotonDefinition()) { 15 | return fWaiting; 16 | } 17 | return fUrgent; 18 | } 19 | 20 | bool NESTStackingAction::isUrgentEmpty() { 21 | if (!savedManager) { 22 | std::cerr 23 | << "savedManager not set by NESTStackingAction::ClassifyNewTrack(). " 24 | "Did you set up NESTStackingAction as your stacking action? Did you " 25 | "override ClassifyNewTrack and forget to set savedManager?" 26 | << std::endl; 27 | } 28 | return savedManager->GetNUrgentTrack() == 0; 29 | } 30 | void NESTStackingAction::NewStage() {} -------------------------------------------------------------------------------- /utils/larnest/data/nr_total_yield.csv: -------------------------------------------------------------------------------- 1 | dataset,energy,energy_sl,energy_sh,field,field_sl,field_sh,yield,yield_sl,yield_sh,converted,excluded 2 | SCENE 2015,16.9,1.5,1.5,96.4,0,0,13.1,0.48,0.48,0,0 3 | SCENE 2015,16.9,1.5,1.5,193,0,0,13.39,0.43,0.43,0,0 4 | SCENE 2015,16.9,1.5,1.5,293,0,0,13.32,0.55,0.55,0,0 5 | SCENE 2015,25.4,2.9,3.2,193,0,0,13.72,0.46,0.46,0,0 6 | SCENE 2015,25.4,2.9,3.2,293,0,0,13.55,0.57,0.57,0,0 7 | SCENE 2015,36.1,3.1,3.1,193,0,0,15.19,0.5,0.5,0,0 8 | SCENE 2015,36.1,3.1,3.1,293,0,0,14.88,0.79,0.79,0,0 9 | SCENE 2015,57.3,4.9,5,96.4,0,0,15.03,0.45,0.45,0,0 10 | SCENE 2015,57.3,4.9,5,193,0,0,15.4,0.59,0.59,0,0 11 | SCENE 2015,57.3,4.9,5,293,0,0,15.19,0.54,0.5,0,0 12 | ARIS 2018,7.1,0.18,0.18,200,0,0,15.49,0.51,0.51,0,0 13 | ARIS 2018,13.7,0.34,0.34,200,0,0,14.86,0.48,0.48,0,0 14 | ARIS 2018,17.8,0.98,0.98,200,0,0,14.89,0.65,0.65,0,0 15 | ARIS 2018,21.7,0.54,0.54,200,0,0,14.57,0.31,0.31,0,0 16 | ARIS 2018,40.5,1.01,1.01,200,0,0,15.17,0.38,0.38,0,0 17 | ARIS 2018,65.4,1.64,1.64,200,0,0,15.58,0.53,0.53,0,0 18 | ARIS 2018,98.1,2.45,2.45,200,0,0,16.96,0.39,0.39,0,0 19 | ARIS 2018,117.8,2.95,2.95,200,0,0,17.56,0.36,0.36,0,0 -------------------------------------------------------------------------------- /examples/tracker.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | #recommended code changes: g1 & g1_gas x0.01, correct e- life 5 | #set correct saturation value PHE_MAX in NEST.hh 6 | #S1,S2 calc modes "Waveform" in analysis.hh, and verbosity set 7 | #to off (0 not -1) to eliminate headers from all output text 8 | 9 | mkdir -p muonPulses/ 10 | cd muonPulses/ 11 | rm -f photon_times* track* 12 | cd ../ 13 | 14 | for ((n=0;n<1;n++)); #number of muons to simulate 15 | do 16 | #echo $n 17 | for ((z=1460;z>=0;z-=1)); #top of the liq down to cath grid 18 | do 19 | #echo $z 20 | #(1.122MeV-cm^2/g)(2.8811g/cm^3)(1e3keV/MeV)(0.1cm/mm) 21 | #=323.3 keV (MIP LET from ESTAR, LXe rho NIST) 22 | ./execNEST 1 beta 323.3 323.3 -1 0,0,$z -1 >> muonPulses/tracker$n.txt 2> /dev/null 23 | mv photon_times.txt muonPulses/photon_times_$z.txt 24 | done 25 | cd muonPulses 26 | cat photon_times_*.txt >> photon_times$n.txt #phe not phd 27 | rm photon_times_*.txt 28 | cd .. 29 | 30 | done 31 | 32 | #current zMax above [mm] works for the LZ (or nT) detector 33 | #replace 1st -1 in execNEST above with (uniform) field (or make z_step bigger) 34 | 35 | #this bash loop should be the equivalent (with saturation) of 36 | #./execNEST nMax MIP 1.122 0,0,1460 -1 0,0,0 0 2> /dev/null 37 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | set(MIN_ROOT_VERSION 6.16) 3 | 4 | 5 | # bareNEST 6 | 7 | add_executable(bareNEST bareNEST.cpp) 8 | target_link_libraries(bareNEST PUBLIC NESTCore) 9 | 10 | set(EXAMPLES bareNEST) 11 | 12 | add_subdirectory("LArNEST/") 13 | 14 | # ROOT example 15 | if(BUILD_ROOT) 16 | # https://cliutils.gitlab.io/modern-cmake/examples/root-simple/ 17 | find_package(ROOT ${MIN_ROOT_VERSION} REQUIRED CONFIG) 18 | 19 | add_executable(rootNEST ${PROJECT_SOURCE_DIR}/examples/rootNEST.cpp) 20 | 21 | # List of ROOT targets: https://cliutils.gitlab.io/modern-cmake/chapters/packages/ROOT.html 22 | # target_link_libraries(rootNEST NEST::Core ROOT::Hist ROOT::Core) 23 | # above fails to find libtbb 24 | 25 | include("${ROOT_USE_FILE}") 26 | separate_arguments(ROOT_EXE_LINKER_FLAGS) 27 | target_link_libraries(rootNEST PUBLIC NESTCore ${ROOT_LIBRARIES} ${ROOT_EXE_LINKER_FLAGS}) 28 | 29 | 30 | list(APPEND EXAMPLES rootNEST) 31 | endif() 32 | 33 | add_executable(pulseShape pulseShape.cpp) 34 | target_link_libraries(pulseShape PUBLIC NESTCore) 35 | list(APPEND EXAMPLES pulseShape) 36 | 37 | add_executable(multipleScatter multipleScatter.cpp) 38 | target_link_libraries(multipleScatter PUBLIC NESTCore) 39 | list(APPEND EXAMPLES multipleScatter) 40 | 41 | install( 42 | TARGETS ${EXAMPLES} 43 | RUNTIME DESTINATION bin 44 | ) 45 | -------------------------------------------------------------------------------- /examples/Xe10_ERBand_Luiz.txt: -------------------------------------------------------------------------------- 1 | 2.5 3.5 2.8391 0.013017 0.14 0.0092189 2 | 7.5 7.5 2.6362 0.0080506 0.13265 0.0056927 3 | 12.5 12.5 2.5159 0.0080286 0.12719 0.0056771 4 | 17.5 17.5 2.4603 0.0077905 0.13084 0.0055087 5 | 22.5 22.5 2.4135 0.0083442 0.13144 0.0059002 6 | 27.5 27.5 2.4071 0.0093309 0.14581 0.006598 7 | 32.5 32.5 2.3909 0.0099745 0.15471 0.007053 8 | 37.5 37.5 2.3724 0.0091848 0.13746 0.0064946 9 | 42.5 42.5 2.3657 0.008895 0.14421 0.0062897 10 | 47.5 47.5 2.3418 0.0082386 0.12754 0.0058256 11 | 52.5 52.5 2.3518 0.0091952 0.14029 0.006502 12 | 57.5 57.5 2.3657 0.0092247 0.15327 0.0065229 13 | 62.5 62.5 2.355 0.0081308 0.1371 0.0057493 14 | 67.5 67.5 2.3476 0.0088318 0.14277 0.006245 15 | 72.5 72.5 2.3616 0.0077515 0.12816 0.0054811 16 | 77.5 77.5 2.3625 0.0088277 0.15112 0.0062421 17 | 82.5 82.5 2.3577 0.0091088 0.15563 0.0064409 18 | 87.5 87.5 2.3544 0.0090764 0.15092 0.006418 19 | 92.5 92.5 2.3469 0.0085974 0.14411 0.0060793 20 | 97.5 97.5 2.3683 0.0091594 0.15636 0.0064767 21 | 102.5 102.5 2.3683 0.0079882 0.14555 0.0056485 22 | 107.5 107.5 2.3706 0.009587 0.17122 0.006779 23 | 112.5 112.5 2.3847 0.0086522 0.16064 0.006118 24 | 117.5 117.5 2.3766 0.0084323 0.1484 0.0059625 25 | 122.5 122.5 2.4104 0.0087889 0.15907 0.0062147 26 | 127.5 127.5 2.4047 0.0085682 0.15403 0.0060586 27 | 132.5 132.5 2.4217 0.0094066 0.1758 0.0066514 28 | 137.5 137.5 2.4313 0.0090307 0.16422 0.0063857 29 | 142.5 142.5 2.4158 0.0083511 0.15665 0.0059051 30 | 147.5 147.5 2.4429 0.0090608 0.16494 0.0064069 31 | 152.5 152.5 2.4395 0.00831 0.15721 0.0058761 32 | 157.5 157.5 2.4472 0.0090435 0.16338 0.0063947 33 | 162.5 162.5 2.4532 0.0087465 0.15839 0.0061847 34 | -------------------------------------------------------------------------------- /examples/Xe10_NRBand_Luiz.txt: -------------------------------------------------------------------------------- 1 | 2.5 3.5 2.4437 0.0040915 0.22011 0.0028933 2 | 7.5 7.5 2.266 0.0036791 0.15411 0.0026015 3 | 12.5 12.5 2.1817 0.0044877 0.12095 0.0031733 4 | 17.5 17.5 2.1317 0.0053533 0.10723 0.0037853 5 | 22.5 22.5 2.0868 0.0054642 0.095396 0.0038638 6 | 27.5 27.5 2.063 0.0052987 0.078394 0.0037467 7 | 32.5 32.5 2.0207 0.0053839 0.072791 0.003807 8 | 37.5 37.5 1.9829 0.0055323 0.063217 0.003912 9 | 42.5 42.5 1.9441 0.0058462 0.064483 0.0041339 10 | 47.5 47.5 1.9453 0.0057811 0.062069 0.0040878 11 | 52.5 52.5 1.8986 0.0058504 0.06052 0.0041368 12 | 57.5 57.5 1.9062 0.0063027 0.051799 0.0044567 13 | 62.5 62.5 1.8692 0.0065493 0.055534 0.004631 14 | 67.5 67.5 1.8585 0.0067361 0.05345 0.0047631 15 | 72.5 72.5 1.8304 0.0059694 0.048802 0.0042211 16 | 77.5 77.5 1.7981 0.0054479 0.036546 0.0054479 17 | 82.5 82.5 1.7953 0.010289 0.062595 0.0072756 18 | 87.5 87.5 1.77 0.011013 0.060806 0.0077872 19 | 92.5 92.5 1.7693 0.0076679 0.040608 0.0054249 20 | 97.5 97.5 1.7464 0.0083107 0.047646 0.0059256 21 | 102.5 102.5 1.7387 0.0076636 0.037622 0.0076636 22 | 107.5 107.5 1.7327 0.0070996 0.03847 0.0059338 23 | 112.5 112.5 1.7363 0.0066929 0.032098 0.0066929 24 | 117.5 117.5 1.7316 0.010464 0.053387 0.007417 25 | 122.5 122.5 1.6794 0.0077322 0.030364 0.0062722 26 | 127.5 127.5 1.692 0.0076869 0.032006 0.0041149 27 | 132.5 132.5 1.6976 0.015203 0.058379 0.010746 28 | 137.5 137.5 1.6861 0.013655 0.052363 0.0096452 29 | 142.5 142.5 1.6736 0.011366 0.047932 0.0081223 30 | 147.5 147.5 1.6312 0.011456 0.050702 0.0080515 31 | 152.5 152.5 1.6405 0.011834 0.025858 0.0056626 32 | 157.5 157.5 1.63 0.020005 0.025026 0.0072933 33 | 162.5 162.5 1.6361 0.017181 0.059916 0.012147 34 | -------------------------------------------------------------------------------- /utils/larnest/data/nr_charge_yield_alt.csv: -------------------------------------------------------------------------------- 1 | dataset,energy,energy_sl,energy_sh,field,field_sl,field_sh,yield,yield_sl,yield_sh,converted,excluded 2 | SCENE 2015,16.9,1.5,1.5,96.4,0,0,3.00,0.20,0.20,0,0 3 | SCENE 2015,16.9,1.5,1.5,193,0,0,3.68,0.20,0.20,0,0 4 | SCENE 2015,16.9,1.5,1.5,293,0,0,4.23,0.27,0.27,0,0 5 | SCENE 2015,16.9,1.5,1.5,486,0,0,4.68,0.24,0.24,0,0 6 | SCENE 2015,25.4,2.9,3.2,193,0,0,3.00,0.17,0.17,0,0 7 | SCENE 2015,25.4,2.9,3.2,293,0,0,3.45,0.20,0.20,0,0 8 | SCENE 2015,25.4,2.9,3.2,486,0,0,3.87,0.20,0.20,0,0 9 | SCENE 2015,36.1,3.1,3.1,193,0,0,2.45,0.13,0.13,0,0 10 | SCENE 2015,36.1,3.1,3.1,293,0,0,2.81,0.17,0.17,0,0 11 | SCENE 2015,36.1,3.1,3.1,486,0,0,3.16,0.14,0.14,0,0 12 | SCENE 2015,57.3,4.9,5.0,96.4,0,0,1.42,0.13,0.13,0,0 13 | SCENE 2015,57.3,4.9,5.0,193,0,0,1.84,0.10,0.10,0,0 14 | SCENE 2015,57.3,4.9,5.0,293,0,0,2.06,0.13,0.13,0,0 15 | SCENE 2015,57.3,4.9,5.0,486,0,0,2.35,0.17,0.17,0,0 16 | Joshi 2015,6.7,0,0,240,13,13,3.60,1.10,0.51,0,0 17 | Joshi 2015,6.7,0,0,640,35,35,4.90,1.22,0.61,0,0 18 | Joshi 2015,6.7,0,0,1600,90,90,5.90,1.41,0.73,0,0 19 | Joshi 2015,6.7,0,0,2130,120,120,6.30,1.63,0.81,0,0 20 | Bondar 2017,233,0.00,0.00,2300,0,0,9.7,1.3,1.3,0,1 21 | Bondar 2017,80,2.00,2.00,2300,0,0,7.8,1.1,1.1,0,1 22 | ARIS 2018,7.1,0.18,0.18,200,0,0,6.14,0.52,0.52,0,0 23 | ARIS 2018,13.7,0.34,0.34,200,0,0,4.34,0.36,0.36,0,0 24 | ARIS 2018,17.8,0.98,0.98,200,0,0,3.67,0.36,0.36,0,0 25 | ARIS 2018,21.7,0.54,0.54,200,0,0,3.35,0.23,0.23,0,0 26 | ARIS 2018,40.5,1.01,1.01,200,0,0,2.33,0.17,0.17,0,0 27 | ARIS 2018,65.4,1.64,1.64,200,0,0,1.64,0.14,0.14,0,0 28 | ARIS 2018,98.1,2.45,2.45,200,0,0,1.37,0.12,0.12,0,0 29 | ARIS 2018,117.8,2.95,2.95,200,0,0,1.22,0.08,0.08,0,0 -------------------------------------------------------------------------------- /examples/efficiency.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | 4 | for e in 0.4 0.41 0.42 0.43 0.44 0.45 0.46 0.47 0.48 0.49 0.5 0.52 0.54 0.56 0.58 0.6 0.62 0.64 0.66 0.68 0.7 0.72 0.74 0.76 0.78 0.8 0.82 0.84 0.86 0.88 0.9 0.92 0.94 0.96 0.98 1 1.05 1.1 1.15 1.2 1.25 1.3 1.35 1.4 1.45 1.5 1.55 1.6 1.65 1.7 1.75 1.8 1.85 1.9 1.95 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5 5.2 5.4 5.6 5.8 6 6.2 6.4 6.6 6.8 7 7.2 7.4 7.6 7.8 8 8.2 8.4 8.6 8.8 9 9.2 9.4 9.6 9.8 10 10.5 11 11.5 12 12.5 13 13.5 14 14.5 15 15.5 16 16.5 17 17.5 18 18.5 19 19.5 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 50.5 51 51.5 52 52.5 53 53.5 54 54.5 55 55.5 56 56.5 57 57.5 58 58.5 59 59.5 60 60.5 61 61.5 62 62.5 63 63.5 64 64.5 65 65.5 66 66.5 67 67.5 68 68.5 69 69.5 70 71 72 73 74 75 76 77 78 79 80 82 84 86 88 90 95 100 5 | #add lower Es even as low as 0.1 (keV) if doing ER, and reduce high end to ~20-40 6 | 7 | do 8 | #Uncomment the echo statement and comment the execution line out to get the energies printed out, to e.g. add to a text output file after the fact for better organization 9 | #echo $e 10 | /Users/szydagis/Desktop/buildNEST/execNEST 1e5 NR $e $e 192 -1 0 > /dev/null 11 | #Don't forget to change the above to your executable's absolute path, and change NR to beta for doing ER instead. Change 192 to your detector's drift E-field (in V/cm) 12 | #1e5: note 1e6 is also a reasonable number of events. Make sure to use the "2>" operator to redirect the terminal screen output of this script to a text file to save it. 13 | 14 | done 15 | 16 | #Lastly, copy/paste results from Excel into txt in terminal, using nest/examples/LUXRun03_nrEff_Simulated.txt as example 17 | -------------------------------------------------------------------------------- /utils/larnest/plot_benchmarks.py: -------------------------------------------------------------------------------- 1 | """ 2 | Benchmark functions for LArNEST 3 | """ 4 | import numpy as np 5 | from matplotlib import pyplot as plt 6 | import pandas as pd 7 | import os 8 | 9 | from parameters import * 10 | from lar_dataset import LArDataset 11 | 12 | if __name__ == "__main__": 13 | 14 | dataset_file = "data/lar_data.npz" 15 | mean_yields_benchmarks_file = "data/mean_yields_benchmarks.csv" 16 | fluctuations_benchmarks_file = "data/fluctuation_benchmarks.csv" 17 | lar_datasets = [] 18 | 19 | # create the dataset object 20 | lar_datasets.append(LArDataset( 21 | dataset_file=dataset_file, 22 | benchmarks_file=mean_yields_benchmarks_file, 23 | plot_config=default_plot_config, 24 | fluctuations=False, 25 | )) 26 | lar_datasets.append(LArDataset( 27 | dataset_file=dataset_file, 28 | benchmarks_file=fluctuations_benchmarks_file, 29 | plot_config=default_plot_config, 30 | fluctuations=True, 31 | )) 32 | 33 | # make benchmark plots 34 | lar_datasets[0].plot_all_yields() 35 | lar_datasets[1].plot_all_fluctuations() 36 | 37 | for ii, lar_dataset in enumerate(lar_datasets): 38 | # plot with data 39 | # nr yields 40 | lar_dataset.plot_data('nr_total') 41 | lar_dataset.plot_data_grid('nr_total') 42 | lar_dataset.plot_data('nr_charge') 43 | lar_dataset.plot_data_grid('nr_charge') 44 | lar_dataset.plot_data('nr_light') 45 | lar_dataset.plot_data_grid('nr_light') 46 | 47 | # er yields 48 | lar_dataset.plot_data('er_charge') 49 | lar_dataset.plot_data_grid('er_charge') 50 | lar_dataset.plot_data('er_light') 51 | lar_dataset.plot_data_grid('er_light') -------------------------------------------------------------------------------- /include/NEST/GammaHandler.hh: -------------------------------------------------------------------------------- 1 | #ifndef GAMMAHANDLER_HH 2 | #define GAMMAHANDLER_HH 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "RandomGen.hh" 11 | #include 12 | #include 13 | #include 14 | //#include "GammaContainer.hh" 15 | using namespace std; 16 | 17 | class GammaHandler { 18 | public: 19 | GammaHandler(){}; 20 | /* 21 | The main function that combines all spectra from photoionization, compton 22 | scattering, pair production, etc... Takes min and max energies, a vector of 23 | monoenergetic gamma energies and a vector of their branching ratios. The index 24 | of the gamma energy must correspond with the index of the branch ratio 25 | */ 26 | const vector combineSpectra(double emin, double emax, string source); 27 | 28 | /* 29 | Get y value for given x value in xyTry. Function is just delta functions at 30 | the gammaEnergies with amplitudes given by yMax*branchRatio at that energy 31 | */ 32 | double photoIonization(const vector>& sourceInfo, 33 | const vector& xyTry); 34 | 35 | /*Return compton spectrum from KN formula and shifted energies */ 36 | double compton(const vector>& sourceInfo, 37 | const vector& xyTry); 38 | 39 | /*Return pair production spectrum from pair production energy equation */ 40 | double pairProduction(const vector>& sourceInfo, 41 | const vector& xyTry); 42 | 43 | /*return energies, branching ratios, and mass attenuation coefficients for a 44 | * given source*/ 45 | 46 | const vector>& sourceLookupTable(const std::string& source); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /examples/loopNEST.in: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env bash 3 | #This code doesn't actually perform any model fitting, however, 4 | #it provides an example for using rootNEST if one wishes to use execNEST + rootNEST 5 | #to fit a data target to find optimal model and/or detector params to match data 6 | 7 | #Search the word "loopNEST" in src/execNEST.cpp for more info on what each of these input arguments is doing 8 | 9 | #First declare ranges for each of the free parameters 10 | #Here, in several for-loops, using 11 parameters with set ranges. 3H Run3 best: .004-46 43-4 -.525-2 .772-5 73-5 0-0.3 2.94-7 .09-.1 .54 .65 ~1. 2e5 skew S1 Hybrid 11 | for a in 0.001 0.002 0.005 #step size in mm 12 | do 13 | for b in 30 40 50 #base for dE/dx power law at high energies 14 | do 15 | for c in -1 -0.75 -0.5 #exponent for dE/dx power law 16 | do 17 | for d in 0.1 0.2 0.5 #variation in dE/dx e.g. 0.1 means 10% 18 | do 19 | for e in 72 73 74 #total quanta per keV i.e. 1/W 20 | do 21 | for f in 0.01 0.02 0.05 #exciton-ion ratio Nex/Ni 22 | do 23 | for g in 1 2 3 #replacement of 1 in ln(1+xi)/xi in mod TIB model 24 | do 25 | for h in 0.02 0.05 0.10 #base for Ni, E, or dE/dx power law in TIB 26 | do 27 | for i in 0.1 0.2 0.5 #exponent 28 | do 29 | for j in 0.2 0.5 1.0 #Fano factor for total quanta 30 | do 31 | for k in 1.0 2.0 5.0 #Fano factor for scintillation 32 | do 33 | 34 | #echo $a $b $c $d $e $f $g $h $i $j $k 35 | 36 | #below: run execNEST with the selected parameters, and save the output 37 | @CMAKE_BINARY_DIR@/execNEST $a $b $c $d $e $f $g $h $i $j $k > nohup.dat 2> nohup.err 38 | 39 | #next, use rootNEST in FIT mode (i.e. mode=1) to get goodness-of-fit statistic with a data file (See README on rootNEST) 40 | @CMAKE_BINARY_DIR@/examples/rootNEST nohup.dat @CMAKE_SOURCE_DIR@/examples/LUXRun03_CH3TBand_SpikeFull.txt 2>> nohup.err 41 | 42 | #then remove the output file and move on to the next parameter set 43 | rm nohup.dat &> /dev/null 44 | 45 | done 46 | done 47 | done 48 | done 49 | done 50 | done 51 | done 52 | done 53 | done 54 | done 55 | done 56 | -------------------------------------------------------------------------------- /include/NEST/TestLArSpectra.hh: -------------------------------------------------------------------------------- 1 | /** 2 | * @file TestLArSpectra.hh 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ValidityTests.hh" 21 | #include "RandomGen.hh" 22 | 23 | class TestLArSpectra { 24 | public: 25 | TestLArSpectra() = default; 26 | // struct WIMP_spectrum_prep { 27 | // double base[100] = {1.}; 28 | // double exponent[100] = {0.}; 29 | // double integral = 0.; 30 | // double xMax = 0.; 31 | // double divisor = 1.; 32 | // }; 33 | // WIMP_spectrum_prep wimp_spectrum_prep; 34 | 35 | // static double CH3T_spectrum(double emin, double emax); 36 | // static double C14_spectrum(double emin, double emax); 37 | // static double B8_spectrum(double emin, double emax); 38 | // static double AmBe_spectrum(double emin, double emax); 39 | // static double Cf_spectrum(double emin, double emax); 40 | // static double DD_spectrum(double xMin, double xMax, double expFall, double 41 | // peakFrac, double peakMu, double peakSig); static double 42 | // ppSolar_spectrum(double emin, double emax); static double 43 | // atmNu_spectrum(double emin, double emax); static double WIMP_dRate(double 44 | // ER, double mWimp, double day); static WIMP_spectrum_prep 45 | // WIMP_prep_spectrum(double mass, double eStep, 46 | // double day); 47 | // static double WIMP_spectrum(WIMP_spectrum_prep wprep, double mass, 48 | // double day); 49 | // static const vector Gamma_spectrum(double xMin, double xMax, 50 | // string source); 51 | 52 | // static double 53 | // ZeplinBackground(); // an example of how to do a better (non-flat) ER 54 | // // BG spectrum for a WS, from Henrique Araujo 55 | }; -------------------------------------------------------------------------------- /include/NEST/RandomGen.hh: -------------------------------------------------------------------------------- 1 | // 2 | // RandomGen.hh 3 | // 4 | // Created by Jacob Cutter, May 10 2018 5 | 6 | #ifndef RANDOMGEN_HH 7 | #define RANDOMGEN_HH 1 8 | #include "xoroshiro.hh" 9 | #include "gcem.hpp" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | class RandomGen { 18 | public: 19 | static RandomGen* rndm(); 20 | void SetSeed(uint64_t s); 21 | void LockSeed(); 22 | void UnlockSeed(); 23 | double rand_uniform(); 24 | double rand_gauss(double mean, double sigma, bool zero_min = false); 25 | double rand_zero_trunc_gauss(double mean, double sigma); 26 | double FindNewMean(double sigma); // shift Gaussian of mean 1 for 0 27 | // truncation 28 | double rand_exponential(double half_life, double t_min = -1, 29 | double t_max = -1); 30 | double rand_skewGauss(double xi, double omega, double alpha); 31 | uint64_t poisson_draw(double mean); 32 | int64_t binom_draw(int64_t N0, double prob); 33 | int integer_range(int min, int max); 34 | vector VonNeumann(double xMin, double xMax, double yMin, double yMax, 35 | double xTest, double yTest, double fValue); 36 | int SelectRanXeAtom(); 37 | 38 | private: 39 | // Random number generator object for this class only 40 | // std::ranlux24 rng; 41 | xoroshiro128plus64 rng; 42 | bool rng_locked = false; 43 | 44 | static constexpr double xoroshiro128plus64_min = 45 | static_cast(xoroshiro128plus64::min()); 46 | static constexpr double xoroshiro128plus64_minmax = static_cast( 47 | xoroshiro128plus64::max() - xoroshiro128plus64::min()); 48 | 49 | static constexpr double two_PI = 2. * M_PI; 50 | static constexpr double four_minus_PI_div_2 = 0.5 * (4. - M_PI); 51 | static constexpr double sqrt2 = gcem::sqrt(2.); 52 | static constexpr double sqrt2_PI = gcem::sqrt(2. * M_PI); 53 | static constexpr double sqrt2_div_PI = gcem::sqrt(2. / M_PI); 54 | static constexpr double log2 = gcem::log(2.); 55 | 56 | RandomGen() = default; // private so that it cannot be manually called 57 | RandomGen(RandomGen const&); // copy constructor is private 58 | void operator=(RandomGen const&); // assignment operator is private 59 | static RandomGen* m_pInstance; // private class pointer instance 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /examples/LArNEST/LArNESTNeutronCapture.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file LArNESTNeutronCapture.cpp 3 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-05-23 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "LArDetector.hh" 15 | #include "LArNEST.hh" 16 | 17 | int main(int argc, char* argv[]) { 18 | double density = 1.393; 19 | uint64_t seed = 0; 20 | if (argc > 1) { 21 | density = atof(argv[1]); 22 | } 23 | if (argc > 2) { 24 | seed = atoi(argv[2]); 25 | } 26 | 27 | std::vector electric_field = {1, 100, 200, 500, 600, 1000, 28 | 1500, 2500, 6000, 9000, 9500}; 29 | 30 | std::vector energy_vals = { 31 | 167.3, 516.0, 348.7, 867.3, 837.7, 1186.8, 1354.0, 1044.3, 32 | 1881.5, 2229.5, 2566.1, 2432.5, 2781.8, 2842.5, 3111.3, 1972.6, 33 | 2291.6, 2810.5, 3564.7, 3405.5, 2668.1, 3564.5, 2614.3, 3451.8, 34 | 4102.5, 1828.8, 2130.7, 2668.1, 2771.8, 3089.4, 3150.2, 3365.5, 35 | 3405.3, 3700.4, 4745.0, 5063.7, 5582.0, 6098.9}; 36 | 37 | LArDetector* detector = new LArDetector(); 38 | // Construct NEST class using detector object 39 | NEST::LArNEST larnest(detector); 40 | // Set random seed of RandomGen class 41 | RandomGen::rndm()->SetSeed(seed); 42 | 43 | // Construct NEST objects for storing calculation results 44 | NEST::LArNESTResult result; 45 | std::ofstream output_file; 46 | 47 | output_file.open("neutron_capture.csv"); 48 | output_file << "type,energy,efield,TotalYield,QuantaYield,LightYield,Nph,Ne," 49 | "Nex,Nion\n"; 50 | 51 | for (size_t v = 0; v < electric_field.size(); v++) { 52 | // iterate over energy values 53 | for (size_t i = 0; i < energy_vals.size(); i++) { 54 | result = larnest.FullCalculation(NEST::LArInteraction::ER, energy_vals[i], 55 | 0, electric_field[v], density, false); 56 | output_file << "ER,"; 57 | output_file << energy_vals[i] << ","; 58 | output_file << electric_field[v] << ","; 59 | output_file << result.yields.TotalYield << ","; 60 | output_file << result.yields.QuantaYield << ","; 61 | output_file << result.yields.LightYield << ","; 62 | output_file << result.yields.Nph << ","; 63 | output_file << result.yields.Ne << ","; 64 | output_file << result.yields.Nex << ","; 65 | output_file << result.yields.Nion << "\n"; 66 | } 67 | } 68 | output_file.close(); 69 | 70 | return 0; 71 | } -------------------------------------------------------------------------------- /utils/larnest/data/alpha_light_yield.csv: -------------------------------------------------------------------------------- 1 | dataset,energy,energy_sl,energy_sh,field,field_sl,field_sh,yield,yield_sl,yield_sh,converted,excluded 2 | Hitachi 1987,5305,0,0,200.198,0,0,32.52220339,1.711694915,1.711694915,0,0 3 | Hitachi 1987,5305,0,0,439.103,0,0,32.72476271,1.722355932,1.722355932,0,0 4 | Hitachi 1987,5305,0,0,2006.01,0,0,33.04035593,1.738966102,1.738966102,0,0 5 | Hitachi 1987,5305,0,0,2413.73,0,0,32.77854237,1.725186441,1.725186441,0,0 6 | Hitachi 1987,5305,0,0,3774.03,0,0,32.31127119,1.70059322,1.70059322,0,0 7 | Hitachi 1987,5305,0,0,5576.19,0,0,31.61107288,1.663740678,1.663740678,0,0 8 | Hitachi 1987,5305,0,0,6562.72,0,0,31.37682542,1.651411864,1.651411864,0,0 9 | Hitachi 1987,5305,0,0,7617.08,0,0,31.05540339,1.634494915,1.634494915,0,0 10 | Hitachi 1987,5305,0,0,8603.26,0,0,30.70512712,1.616059322,1.616059322,0,0 11 | Hitachi 1987,5305,0,0,10474.1,0,0,30.20784237,1.589886441,1.589886441,0,0 12 | Hitachi 1987,5305,0,0,12310.6,0,0,29.59459322,1.557610169,1.557610169,0,0 13 | Hitachi 1987,5305,0,0,14318.2,0,0,29.30009322,1.542110169,1.542110169,0,0 14 | Hitachi 1987,5305,0,0,16257,0,0,28.74464915,1.512876271,1.512876271,0,0 15 | Hitachi 1987,5305,0,0,18162.4,0,0,28.42139153,1.495862712,1.495862712,0,0 16 | Hitachi 1987,5305,0,0,20101.4,0,0,27.95296102,1.471208475,1.471208475,0,0 17 | Hitachi 1987,5305,0,0,21973,0,0,27.71681356,1.458779661,1.458779661,0,0 18 | Hitachi 1987,5305,0,0,23979.9,0,0,27.16120847,1.429537288,1.429537288,0,0 19 | Hitachi 1987,6120,0,0,780.308,0,0,33.85299153,1.50486107,1.50486107,0,0 20 | Hitachi 1987,6120,0,0,1460.46,0,0,33.61337288,1.494209344,1.494209344,0,0 21 | Hitachi 1987,6120,0,0,1733.75,0,0,33.94024576,1.508739767,1.508739767,0,0 22 | Hitachi 1987,6120,0,0,4147.98,0,0,32.98210169,1.466147557,1.466147557,0,0 23 | Hitachi 1987,6120,0,0,4794.17,0,0,32.77222881,1.456818115,1.456818115,0,0 24 | Hitachi 1987,6120,0,0,9419.32,0,0,31.18380508,1.386208195,1.386208195,0,0 25 | Hitachi 1987,6120,0,0,14079,0,0,29.77395508,1.323536381,1.323536381,0,0 26 | Hitachi 1987,6120,0,0,18977.7,0,0,28.57199492,1.270105857,1.270105857,0,0 27 | Hitachi 1987,6120,0,0,23570.7,0,0,27.63867203,1.22861702,1.22861702,0,0 28 | Hitachi 1987,6120,0,0,28334.3,0,0,26.82410085,1.19240703,1.19240703,0,0 29 | Hitachi 1987,6120,0,0,33098.3,0,0,26.15842373,1.162815803,1.162815803,0,0 30 | Hitachi 1987,6120,0,0,37828.2,0,0,25.46300085,1.131902292,1.131902292,0,0 31 | Agnes 2016,6000,0,0,0,0,0,31.55084746,1.742267564,1.742267564,0,0 32 | Agnes 2016,6000,0,0,50,0,0,31.70860169,0.2161016949,0.2161016949,0,0 33 | Agnes 2016,6000,0,0,100,0,0,31.6455,0.1728813559,0.1728813559,0,0 34 | Agnes 2016,6000,0,0,150,0,0,31.92945763,0.2161016949,0.2161016949,0,0 35 | Agnes 2016,6000,0,0,200,0,0,32.18186441,0.08644067797,0.08644067797,0,0 -------------------------------------------------------------------------------- /include/NEST/TestSpectra.hh: -------------------------------------------------------------------------------- 1 | /* 2 | * File: TestSpectra.hh 3 | * Author: brodsky3 4 | * 5 | * Created on December 11, 2017, 10:27 AM 6 | */ 7 | 8 | #ifndef TESTSPECTRA_HH 9 | #define TESTSPECTRA_HH 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ValidityTests.hh" 21 | #include "RandomGen.hh" 22 | 23 | #define NEST_AVO \ 24 | 6.0221409e+23 // good to keep in sync w/ NEST.hh, can't define twice 25 | #define ATOM_NUM 54. // ibid. 26 | 27 | #define RHO_NAUGHT 0.3 // local DM halo density in [GeV/cm^3]. Lewin & Smith 28 | #define V_SUN 250.2 // +/- 1.4: arXiv:2105.00599, page 12 (V_EARTH 29.8km/s) 29 | #define V_WIMP 238. // +/- 1.5: page 12 and Table 1 30 | #define V_ESCAPE 544. // M.C. Smith et al. RAVE Survey 31 | 32 | #define NUMBINS_MAX 1000 33 | 34 | static constexpr double ElectronRestMassEnergy = 510.9989461; 35 | 36 | class TestSpectra { 37 | public: 38 | TestSpectra() = default; 39 | ; // private so that it cannot be manually called 40 | 41 | struct WIMP_spectrum_prep { 42 | double base[100] = {1.}; 43 | double exponent[100] = {0.}; 44 | double integral = 0.; 45 | double xMax = 0.; 46 | double divisor = 1.; 47 | }; 48 | WIMP_spectrum_prep wimp_spectrum_prep; 49 | 50 | static double CH3T_spectrum(double emin, double emax); 51 | static double C14_spectrum(double emin, double emax); 52 | static double B8_spectrum(double emin, double emax); 53 | static double AmBe_spectrum(double emin, double emax); 54 | static double PowLawFit_spectrum(double emin, double emax, double exp); 55 | static double Cf_spectrum(double emin, double emax); 56 | static double DD_spectrum(double xMin, double xMax, double expFall, 57 | double peakFrac, double peakMu, double peakSig, double peakSkew); 58 | static double ppSolar_spectrum(double emin, double emax); 59 | static double atmNu_spectrum(double emin, double emax); 60 | static double WIMP_dRate(double ER, double mWimp, double day); 61 | static WIMP_spectrum_prep WIMP_prep_spectrum(double mass, double eStep, 62 | double day); 63 | static double WIMP_spectrum(WIMP_spectrum_prep wprep, double mass, 64 | double day); 65 | static const vector Gamma_spectrum(double xMin, double xMax, 66 | string source); 67 | 68 | static double 69 | ZeplinBackground(); // an example of how to do a better (non-flat) ER 70 | // BG spectrum for a WS, from Henrique Araujo 71 | }; 72 | 73 | #endif /* TESTSPECTRA_HH */ 74 | -------------------------------------------------------------------------------- /.zenodo.json: -------------------------------------------------------------------------------- 1 | { 2 | "language": "eng", 3 | "license": "Apache-2.0", 4 | "title": "Noble Element Simulation Technique", 5 | "keywords": [ 6 | "dark matter", 7 | "microphysics", 8 | "xenon", 9 | "argon" 10 | ], 11 | "creators": [ 12 | { 13 | "orcid": "0000-0002-9334-4659", 14 | "affiliation": "University at Albany, SUNY", 15 | "name": "Szydagis, M." 16 | }, 17 | { 18 | "affiliation": "RPI", 19 | "name": "Brown, E." 20 | }, 21 | { 22 | "affiliation": "University of California, Davis", 23 | "name": "Carrara, N." 24 | }, 25 | { 26 | "affiliation": "University of California, Los Angeles", 27 | "name": "Kamaha, A.C." 28 | }, 29 | { 30 | "affiliation": "NRNU MEPhI and ITEP", 31 | "name": "Kozlova, E.S." 32 | }, 33 | { 34 | "affiliation": "University of California, Berkeley", 35 | "name": "McKinsey, D.N." 36 | }, 37 | { 38 | "affiliation": "RPI", 39 | "name": "McMichael, K." 40 | }, 41 | { 42 | "affiliation": "University at Albany, SUNY", 43 | "name": "McMonigle, R." 44 | }, 45 | { 46 | "affiliation": "Colorado State University", 47 | "name": "Mooney, M." 48 | }, 49 | { 50 | "affiliation": "Colorado State University", 51 | "name": "Mueller, J." 52 | }, 53 | { 54 | "affiliation": "University of California, Davis", 55 | "name": "Mulhearn, M." 56 | }, 57 | { 58 | "affiliation": "University of California, Riverside", 59 | "name": "Murray, W." 60 | }, 61 | { 62 | "affiliation": "University of California, San Diego", 63 | "name": "Ni, K." 64 | }, 65 | { 66 | "affiliation": "University of Michigan", 67 | "name": "Rischbieter, G.R.C." 68 | }, 69 | { 70 | "affiliation": "University of California, Los Angeles", 71 | "name": "Trengove, K." 72 | }, 73 | { 74 | "orcid": "0000-0001-8158-7795", 75 | "affiliation": "Rice University", 76 | "name": "Tunnell, C.D." 77 | }, 78 | { 79 | "affiliation": "University of California, Berkeley", 80 | "name": "Velan, V." 81 | }, 82 | { 83 | "affiliation": "University of California, Riverside", 84 | "name": "Westerdale, S." 85 | }, 86 | { 87 | "affiliation": "University of California, San Diego", 88 | "name": "Zhong, M." 89 | } 90 | ], 91 | "access_right": "open", 92 | "description": "

Fast C++ simulation of different particle types in liquid, gaseous, and solid xenon

\n\n
    \n\t
  • Mean scintillation light and ionization charge yields
  • \n\t
  • Variation in total quanta, and recombination fluctuations
  • \n\t
  • Dependencies on energy, electric field strength, and density
  • \n\t
  • Pulse shape models for both S1 and S2, including e-trains
  • \n\t
  • Additional tools, for calculating leakage and limits
  • \n
\n\n

Not a complete list, please see the exhaustive README file included.

" 93 | } 94 | -------------------------------------------------------------------------------- /src/LArDetector.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file LArDetector.cpp 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #include "LArDetector.hh" 10 | 11 | LArDetector::LArDetector() { Initialization(); } 12 | 13 | void LArDetector::Initialization() { 14 | // Primary Scintillation (S1) parameters 15 | // phd per S1 phot at dtCntr (not phe). Divide out 2-PE effect 16 | double g1 = 0.0760; 17 | // single phe resolution (Gaussian assumed) 18 | double sPEres = 0.58; 19 | // POD threshold in phe, usually used IN PLACE of sPEeff 20 | double sPEthr = 0.35; 21 | // actual efficiency, can be used in lieu of POD threshold 22 | double sPEeff = 1.00; 23 | double noiseBaseline[4] = { 24 | 0.0, // baseline noise mean and width in PE (Gaussian) 25 | 0.0, // baseline noise mean and width in PE (Gaussian) 26 | 0.0, // EXO noise mean 27 | 0.0 // EXO noise width 28 | }; 29 | // chance 1 photon makes 2 phe instead of 1 in Hamamatsu PMT 30 | double P_dphe = 0.2; 31 | 32 | double coinWind = 100; // S1 coincidence window in ns 33 | int coinLevel = 2; // how many PMTs have to fire for an S1 to count 34 | int numPMTs = 89; // For coincidence calculation 35 | 36 | bool OldW13eV = true; // for matching EXO-200's W measurement 37 | // "Linear noise" terms as defined in Dahl thesis and by D. McK 38 | // S1->S1 Gaussian-smeared w/ noiseL[0]*S1. Ditto S2 39 | double noiseLinear[2] = {3e-2, 3e-2}; 40 | //(n)EXO quadratic noise term 41 | double noiseQuadratic[2] = {0.0, 0.0}; 42 | 43 | // Ionization and Secondary Scintillation (S2) parameters 44 | double g1_gas = 0.06; // phd per S2 photon in gas, used to get SE size 45 | double s2Fano = 3.61; // Fano-like fudge factor for SE width 46 | // the S2 threshold in phe or PE, *not* phd. Affects NR most 47 | double s2_thr = 300.; 48 | double E_gas = 12.; // field in kV/cm between liquid/gas border and anode 49 | double eLife_us = 2200.; // the drift electron mean lifetime in micro-seconds 50 | 51 | // Thermodynamic Properties 52 | bool inGas = false; 53 | double T_Kelvin = 177.; // for liquid drift speed calculation 54 | double p_bar = 2.14; // gas pressure in units of bars, it controls S2 size 55 | // if you are getting warnings about being in gas, lower T and/or raise p 56 | 57 | // Data Analysis Parameters and Geometry 58 | double dtCntr = 40.; // center of detector for S1 corrections, in usec. 59 | double dt_min = 20.; // minimum. Top of detector fiducial volume 60 | double dt_max = 60.; // maximum. Bottom of detector fiducial volume 61 | 62 | double radius = 50.; // millimeters 63 | double radmax = 50.; 64 | 65 | double TopDrift = 150.; // mm not cm or us (but, this *is* where dt=0) 66 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 67 | // PMTs) 68 | // In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, not 69 | // anode! 70 | double anode = 152.5; // the level of the anode grid-wire plane in mm 71 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 72 | // above it 73 | double gate = 147.5; // mm. This is where the E-field changes (higher) 74 | // in gas detectors, the gate is still the gate, but it's where S2 starts 75 | double cathode = 1.00; // mm. Defines point below which events are gamma-X 76 | 77 | // 2-D (X & Y) Position Reconstruction 78 | double PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 79 | double PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 80 | double molarMass = 131.293; // molar mass, g/mol 81 | } -------------------------------------------------------------------------------- /include/NEST/analysis_G2.hh: -------------------------------------------------------------------------------- 1 | #include "NEST.hh" 2 | 3 | // Verbosity flag (for limiting output to yields; no timing) 4 | int verbosity = 1; // use -1 for off, 0 for eff, and 2 for N-pho model 5 | // Loop for execNEST and rootNEST to find the best-fit model parameters 6 | unsigned loopNEST = 0; 7 | // 0 for no or off, 1 for ER, 2 for NR 8 | // an executable bash script was created in the build directory 9 | bool PrintSubThr = false; //BEWARE that false makes #evts < your req 10 | // include or exclude the infamous negative S1 & S2 pulses (and 0's) 11 | 12 | // General parameters of importance changing the global behavior 13 | bool MCtruthE = true; // false means reconstructed energy 14 | bool MCtruthPos = true; // false means reconstructed position 15 | 16 | // Setting the S1 and S2 calculation modes 17 | NEST::S1CalculationMode s1CalculationMode = NEST::S1CalculationMode::Hybrid; 18 | // S1 calculation mode options are: 19 | // Full [Default]: calculating the pulse area by looping over all the pmt hits 20 | // Parametric: calculating the pulse area by using a parametric equation 21 | // Hybrid: Using Full and Parametric with a transition point: n_pmt_hits > 22 | // n_pmts Waveform: calculating the pulse area with the Full calculation mode 23 | // and the waveform 24 | 25 | NEST::S2CalculationMode s2CalculationMode = NEST::S2CalculationMode::Full; 26 | // S2 calculation mode options are: 27 | // Full [Default]: calculate only the pulse area 28 | // Waveform: calculate the pulse area and the waveform 29 | // WaveformWithEtrain: calculate the pulse area and the waveform with etrain 30 | 31 | // 0 means PE, 1 means phd (PE/~1.2), 2 means spike count 32 | int usePD = 1; 33 | // band style: log(S2) with 1, while 0 means log(S2/S1) 34 | int useS2 = 1; // xtra feature: 2 means S2 x-axis energy scale 35 | 36 | double minS1 = 2.5; // units are controlled by the usePD flag 37 | // this is separate from S1 thresholds controlled by detector 38 | double maxS1 = 120.5; 39 | int numBins = 118; // for LUXRun03 DD, change these to 1.7,110.6,99 40 | 41 | // for efficiency calculation 42 | // minS2 need not match S2 threshold in detector.hh 43 | // you can treat as trigger vs. analysis thresholds 44 | double minS2 = 2e2; 45 | double maxS2 = 9e4; // 5e3 for DD. At least 2e5 for post-Run04 14C 46 | 47 | // log(S2/S1) or log(S2) admitted into analysis incl. limit 48 | double logMax = 5.0; // when skewness=1 or 2 ROOT ignores these and does raw 49 | // mean +/- 3-sigma 50 | double logMin = 2.0; 51 | int logBins = 50; //#bins in between logMin & logMax for fits 52 | 53 | // some numbers for fine-tuning the speed vs. the accuracy 54 | double z_step = 55 | 1.; // mm, for integrating non-uni EF. Larger detectors require larger 56 | // z_step ~0.5-1mm for tonne-scale TPCs like LZ and XENONnT 57 | double E_step = 3.0; // keV, for integrating WIMP spectrum. NEST will warn you 58 | // if choice poor 59 | // Rec >~20GeV 6keV, <~5GeV 0.5keV 60 | 61 | // Set the rootNEST options 62 | int freeParam = 63 | 2; // #free param for calculating DoF in X^2; 2 for Ly and Qy, or g1 and g2 64 | int skewness = 65 | 1; // 1 means skew-Gaussian fits (2 more rigorous fit, more detail output) 66 | int mode = 0; 67 | // 0 default is to provide 1 band (no data comp) or if 2 args ER BG discrim & 68 | // leakage frac 1 outputs GoF for sim band 1st cf. data band 2nd (Gauss 69 | // centroids of hist in S1 slices) 2 outputs wimp masses and cross-sections for 70 | // given efficiency. 3 outputs SI cross-section using adaptive cut and count. 71 | // args are data file, wimp spectrum file (e.g. from execNEST), and reference 72 | // cross section 73 | int NRbandCenter = 74 | 3; // 1 is Gauss mean, 3 is fit to means, 2 is compromise. Neg->medians 75 | -------------------------------------------------------------------------------- /include/NEST/analysis.hh: -------------------------------------------------------------------------------- 1 | 2 | #include "NEST.hh" 3 | 4 | //NOTE - ASK YOURSELF IF YOU SHOULD BE WORKING ON ANALYSIS_G2 INSTEAD! 5 | 6 | // Verbosity flag (for limiting output to yields; no timing) 7 | int verbosity = 1; // use -1 for off, but 0 for efficiency 8 | // Loop for execNEST and rootNEST to find the best-fit model parameters 9 | unsigned loopNEST = 0; 10 | // 0 for no or off, 1 for ER, 2 for NR 11 | // an executable bash script was created in the build directory 12 | bool PrintSubThr = true; //BEWARE that false makes #evts < your req 13 | // include or exclude the infamous negative S1 & S2 pulses (and 0's) 14 | 15 | // General parameters of importance changing the global behavior 16 | bool MCtruthE = false; // false means reconstructed energy 17 | bool MCtruthPos = false; // false means reconstructed position 18 | 19 | // Setting the S1 and S2 calculation modes 20 | NEST::S1CalculationMode s1CalculationMode = NEST::S1CalculationMode::Hybrid; 21 | // S1 calculation mode options are: 22 | // Full [Default]: calculating the pulse area by looping over all the pmt hits 23 | // Parametric: calculating the pulse area by using a parametric equation 24 | // Hybrid: Using Full and Parametric with a transition point: n_pmt_hits > 25 | // n_pmts Waveform: calculating the pulse area with the Full calculation mode 26 | // and the waveform 27 | 28 | NEST::S2CalculationMode s2CalculationMode = NEST::S2CalculationMode::Full; 29 | // S2 calculation mode options are: 30 | // Full [Default]: calculate only the pulse area 31 | // Waveform: calculate the pulse area and the waveform 32 | // WaveformWithEtrain: calculate the pulse area and the waveform with etrain 33 | 34 | // 0 means PE, 1 means phd (PE/~1.2), 2 means spike count 35 | int usePD = 2; 36 | // band style: log(S2) with 1, while 0 means log(S2/S1) 37 | int useS2 = 0; // xtra feature: 2 means S2 x-axis energy scale 38 | 39 | double minS1 = 1.5; // units are controlled by the usePD flag 40 | // this is separate from S1 thresholds controlled by detector 41 | double maxS1 = 99.5; 42 | int numBins = 98; // for LUXRun03 DD, change these to 1.7,110.6,99 43 | 44 | // for efficiency calculation 45 | // minS2 need not match S2 threshold in detector.hh 46 | // you can treat as trigger vs. analysis thresholds 47 | double minS2 = 42.; 48 | double maxS2 = 1e4; // 5e3 for DD. At least 2e5 for post-Run04 14C 49 | 50 | // log(S2/S1) or log(S2) admitted into analysis incl. limit 51 | double logMax = 3.6; // when skewness=1 or 2 ROOT ignores these and does raw 52 | // mean +/- 3-sigma 53 | double logMin = 0.6; 54 | int logBins = 30; //#bins in between logMin & logMax for fits 55 | 56 | // some numbers for fine-tuning the speed vs. the accuracy 57 | double z_step = 58 | 0.1; // mm, for integrating non-uni EF. Larger detectors require larger 59 | // z_step ~0.5-1mm for tonne-scale TPCs like LZ and XENONnT 60 | double E_step = 5.0; // keV, for integrating WIMP spectrum. NEST will warn you 61 | // if choice poor 62 | // Rec >~20GeV 6keV, <~5GeV 0.5keV 63 | 64 | // Set the rootNEST options 65 | int freeParam = 66 | 2; // #free param for calculating DoF in X^2; 2 for Ly and Qy, or g1 and g2 67 | int skewness = 68 | 1; // 1 means skew-Gaussian fits (2 more rigorous fit, more detail output) 69 | int mode = 0; 70 | // 0 default is to provide 1 band (no data comp) or if 2 args ER BG discrim & 71 | // leakage frac 1 outputs GoF for sim band 1st cf. data band 2nd (Gauss 72 | // centroids of hist in S1 slices) 2 outputs wimp masses and cross-sections for 73 | // given efficiency. 3 outputs SI cross-section using adaptive cut and count. 74 | // args are data file, wimp spectrum file (e.g. from execNEST), and reference 75 | // cross section 76 | int NRbandCenter = 77 | 3; // 1 is Gauss mean, 3 is fit to means, 2 is compromise. Neg->medians 78 | -------------------------------------------------------------------------------- /examples/LArNEST/LArNESTMeanYieldsBenchmarks.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file legacyLArNEST.cpp 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief Benchmarks for LAr ER model. This program generates ... 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "LArDetector.hh" 16 | #include "LArNEST.hh" 17 | 18 | int main(int argc, char* argv[]) { 19 | // this sample program takes can take two arguments, 20 | // (optional) 1) density - the density of the LAr to use 21 | // (optional) 2) seed - a seed for the random number generator 22 | 23 | double density = 1.393; 24 | uint64_t seed = 0; 25 | if (argc > 1) { 26 | density = atof(argv[1]); 27 | } 28 | if (argc > 2) { 29 | seed = atoi(argv[2]); 30 | } 31 | 32 | // set up energy steps 33 | int num_energy_steps = 50000; 34 | std::vector energy_vals; 35 | double start_val = .1; 36 | double end_val = 1000; 37 | double step_size = (end_val - start_val) / num_energy_steps; 38 | 39 | for (size_t i = 0; i < num_energy_steps; i++) { 40 | energy_vals.emplace_back(start_val + step_size * i); 41 | } 42 | std::vector particle_types = { 43 | NEST::LArInteraction::NR, NEST::LArInteraction::ER, 44 | NEST::LArInteraction::Alpha}; 45 | std::vector particle_type = {"NR", "ER", "Alpha"}; 46 | 47 | LArDetector* detector = new LArDetector(); 48 | // Construct NEST class using detector object 49 | NEST::LArNEST larnest(detector); 50 | // Set random seed of RandomGen class 51 | RandomGen::rndm()->SetSeed(seed); 52 | 53 | // Construct NEST objects for storing calculation results 54 | NEST::LArNESTResult result; 55 | std::ofstream output_file; 56 | 57 | output_file.open("mean_yields_benchmarks.csv"); 58 | output_file << "type,energy,efield,TotalYield,QuantaYield,LightYield,Nph,Ne," 59 | "Nex,Nion,TotalYield_std,QuantaYield_std,LightYield_std,Nph_" 60 | "std,Ne_std,Nex_std,Nion_std\n"; 61 | 62 | // iterate over electric field values 63 | for (size_t k = 0; k < particle_types.size(); k++) { 64 | std::vector electric_field; 65 | if (particle_types[k] == NEST::LArInteraction::NR) { 66 | electric_field.insert(electric_field.end(), {1, 50, 100, 200, 250, 500, 67 | 1000, 1500, 1750, 2000}); 68 | } else if (particle_types[k] == NEST::LArInteraction::ER) { 69 | electric_field.insert(electric_field.end(), {1, 100, 200, 600, 1000, 1500, 70 | 2500, 6000, 9000, 9500}); 71 | } else { 72 | electric_field.insert(electric_field.end(), 73 | {1, 50, 100, 500, 1000, 5000, 10000, 20000}); 74 | } 75 | for (size_t v = 0; v < electric_field.size(); v++) { 76 | // iterate over energy values 77 | for (size_t i = 0; i < num_energy_steps; i++) { 78 | result = larnest.FullCalculation(particle_types[k], energy_vals[i], 0, 79 | electric_field[v], density, false); 80 | output_file << particle_type[k] << ","; 81 | output_file << energy_vals[i] << ","; 82 | output_file << electric_field[v] << ","; 83 | output_file << result.yields.TotalYield << ","; 84 | output_file << result.yields.QuantaYield << ","; 85 | output_file << result.yields.LightYield << ","; 86 | output_file << result.yields.Nph << ","; 87 | output_file << result.yields.Ne << ","; 88 | output_file << result.yields.Nex << ","; 89 | output_file << result.yields.Nion << ",0,0,0,0,0,0,0\n"; 90 | } 91 | } 92 | } 93 | output_file.close(); 94 | return 0; 95 | } -------------------------------------------------------------------------------- /include/NEST/execNEST.hh: -------------------------------------------------------------------------------- 1 | #ifndef __EXECNEST_H__ 2 | #define __EXECNEST_H__ 1 3 | 4 | #include "NEST.hh" 5 | 6 | using namespace std; 7 | using namespace NEST; 8 | 9 | 10 | class NESTObservableArray { 11 | public: 12 | NESTObservableArray(){}; 13 | ~NESTObservableArray() = default; 14 | 15 | void store_signals( 16 | double energy, 17 | std::vector pos, 18 | NESTresult result, 19 | std::vector s1, 20 | std::vector s2, 21 | std::vector s1_wf_time, 22 | std::vector s1_wf_amp, 23 | std::vector s2_wf_time, 24 | std::vector s2_wf_amp 25 | ); 26 | 27 | // Truth 28 | std::vector energy_kev; 29 | std::vector x_mm; 30 | std::vector y_mm; 31 | std::vector z_mm; 32 | 33 | // Quanta 34 | std::vector n_electrons; 35 | std::vector n_photons; 36 | 37 | // S1 38 | std::vector s1_nhits; // MC-true integer hits in same OR different PMTs, NO double phe effect 39 | std::vector s1_nhits_dpe; // MC-true integer hits WITH double phe effect (Nphe > nHits) 40 | std::vector s1_nhits_thr; // nHits post coincidence window and single PE eff! 41 | std::vector s1r_phe; // smeared DAQ pulse areas in phe, NO XYZ correction 42 | std::vector s1c_phe; // same as s1r_phe WITH XYZ correction 43 | std::vector s1r_phd; // same as s1r_phe, adjusted/corrected *downward* for 2-PE effect (LUX phd units) 44 | std::vector s1c_phd; // same as s1r_phd, but XYZ-corrected 45 | std::vector s1r_spike; // spike count, NO XYZ correction 46 | std::vector s1c_spike; // spike count, WITH XYZ correction 47 | 48 | std::vector> s1_photon_times; // Time that S1 photons reach a PMT (includes undetected photons) 49 | 50 | // S2 51 | std::vector s2_Nee; // integer number of electrons unabsorbed in liquid then getting extracted 52 | std::vector s2_Nph; // raw number of photons produced in the gas gap 53 | std::vector s2_nhits; // MC-true integer hits in same OR different PMTs, NO double phe effect 54 | std::vector s2_nhits_dpe; // MC-true integer hits WITH double phe effect (Nphe > nHits) 55 | std::vector s2r_phe; // smeared DAQ pulse areas in phe, NO XYZ correction 56 | std::vector s2c_phe; // same as s2r_phe WITH XYZ correction 57 | std::vector s2r_phd; // same s2r_phe, adjusted/corrected *downward* for 2-PE effect (LUX phd units) 58 | std::vector s2c_phd; // same as s2r_phd, but XYZ-corrected 59 | 60 | // Waveforms 61 | std::vector> s1_waveform_time; 62 | std::vector> s1_waveform_amp; 63 | std::vector> s2_waveform_time; 64 | std::vector> s2_waveform_amp; 65 | }; 66 | 67 | vector> GetBand(vector S1s, vector S2s, 68 | bool resol, int nFold); 69 | 70 | void GetEnergyRes(vector Es); 71 | 72 | int execNEST(VDetector* detector, double numEvts, const string& type, 73 | double eMin, double eMax, double inField, string position, 74 | const string& posiMuon, double fPos, int seed, bool no_seed, 75 | double dayNumber); 76 | 77 | NESTObservableArray runNESTvec(VDetector* detector, 78 | INTERACTION_TYPE scatterType, 79 | std::vector eList, 80 | std::vector> pos3dxyz, 81 | double inField = -1.0, int seed = 0, 82 | std::vector ERYieldsParam = default_ERYieldsParam, 83 | std::vector NRYieldsParam = default_NRYieldsParam, 84 | std::vector NRERWidthsParam = default_NRERWidthsParam, 85 | S1CalculationMode s1mode = NEST::S1CalculationMode::Full, 86 | S2CalculationMode s2mode = NEST::S2CalculationMode::Full); 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /utils/larnest/data/alpha_charge_yield.csv: -------------------------------------------------------------------------------- 1 | dataset,energy,energy_sl,energy_sh,field,field_sl,field_sh,yield,yield_sl,yield_sh,converted,excluded 2 | Scalettar 1982,5640,0,0,268,0,0,0.46,0,0,0,0 3 | Scalettar 1982,5640,0,0,270,0,0,0.43,0,0,0,0 4 | Scalettar 1982,5640,0,0,328,0,0,0.49,0,0,0,0 5 | Scalettar 1982,5640,0,0,420,0,0,0.58,0,0,0,0 6 | Scalettar 1982,5640,0,0,441,0,0,0.55,0,0,0,0 7 | Scalettar 1982,5640,0,0,461,0,0,0.58,0,0,0,0 8 | Scalettar 1982,5640,0,0,487,0,0,0.59,0,0,0,0 9 | Scalettar 1982,5640,0,0,501,0,0,0.6,0,0,0,0 10 | Scalettar 1982,5640,0,0,533,0,0,0.55,0,0,0,0 11 | Scalettar 1982,5640,0,0,539,0,0,0.61,0,0,0,0 12 | Scalettar 1982,5640,0,0,540,0,0,0.62,0,0,0,0 13 | Scalettar 1982,5640,0,0,556,0,0,0.64,0,0,0,0 14 | Scalettar 1982,5640,0,0,592,0,0,0.65,0,0,0,0 15 | Scalettar 1982,5640,0,0,663,0,0,0.72,0,0,0,0 16 | Scalettar 1982,5640,0,0,669,0,0,0.71,0,0,0,0 17 | Scalettar 1982,5640,0,0,819,0,0,0.78,0,0,0,0 18 | Scalettar 1982,5640,0,0,836,0,0,0.73,0,0,0,0 19 | Scalettar 1982,5640,0,0,836,0,0,0.82,0,0,0,0 20 | Scalettar 1982,5640,0,0,839,0,0,0.79,0,0,0,0 21 | Scalettar 1982,5640,0,0,839,0,0,0.8,0,0,0,0 22 | Scalettar 1982,5640,0,0,860,0,0,0.82,0,0,0,0 23 | Scalettar 1982,5640,0,0,999,0,0,0.93,0,0,0,0 24 | Scalettar 1982,5640,0,0,1004,0,0,0.92,0,0,0,0 25 | Scalettar 1982,5640,0,0,1036,0,0,0.92,0,0,0,0 26 | Scalettar 1982,5640,0,0,1330,0,0,1.05,0,0,0,0 27 | Scalettar 1982,5640,0,0,1414,0,0,1.08,0,0,0,0 28 | Scalettar 1982,5640,0,0,1489,0,0,1.18,0,0,0,0 29 | Scalettar 1982,5640,0,0,1743,0,0,1.29,0,0,0,0 30 | Scalettar 1982,5640,0,0,1905,0,0,1.34,0,0,0,0 31 | Scalettar 1982,5640,0,0,2106,0,0,1.37,0,0,0,0 32 | Scalettar 1982,5640,0,0,2271,0,0,1.46,0,0,0,0 33 | Scalettar 1982,5640,0,0,2306,0,0,1.52,0,0,0,0 34 | Scalettar 1982,5640,0,0,2424,0,0,1.57,0,0,0,0 35 | Scalettar 1982,5640,0,0,2785,0,0,1.71,0,0,0,0 36 | Scalettar 1982,5640,0,0,2969,0,0,1.76,0,0,0,0 37 | Scalettar 1982,5640,0,0,2992,0,0,1.66,0,0,0,0 38 | Scalettar 1982,5640,0,0,2998,0,0,1.71,0,0,0,0 39 | Scalettar 1982,5640,0,0,3191,0,0,1.76,0,0,0,0 40 | Scalettar 1982,5640,0,0,3286,0,0,1.87,0,0,0,0 41 | Scalettar 1982,5640,0,0,3589,0,0,1.9,0,0,0,0 42 | Scalettar 1982,5640,0,0,3777,0,0,1.99,0,0,0,0 43 | Scalettar 1982,5640,0,0,3783,0,0,2.03,0,0,0,0 44 | Scalettar 1982,5640,0,0,3817,0,0,1.94,0,0,0,0 45 | Scalettar 1982,5640,0,0,3998,0,0,1.98,0,0,0,0 46 | Scalettar 1982,5640,0,0,4096,0,0,2.03,0,0,0,0 47 | Scalettar 1982,5640,0,0,4402,0,0,2.15,0,0,0,0 48 | Scalettar 1982,5640,0,0,4446,0,0,2.21,0,0,0,0 49 | Scalettar 1982,5640,0,0,4690,0,0,2.14,0,0,0,0 50 | Scalettar 1982,5640,0,0,4996,0,0,2.18,0,0,0,0 51 | Scalettar 1982,5640,0,0,5304,0,0,2.34,0,0,0,0 52 | Scalettar 1982,5640,0,0,5893,0,0,2.51,0,0,0,0 53 | Scalettar 1982,5640,0,0,6287,0,0,2.5,0,0,0,0 54 | Scalettar 1982,5640,0,0,7199,0,0,2.82,0,0,0,0 55 | Scalettar 1982,5640,0,0,7492,0,0,2.8,0,0,0,0 56 | Scalettar 1982,5640,0,0,7786,0,0,2.84,0,0,0,0 57 | Scalettar 1982,5640,0,0,8582,0,0,3.03,0,0,0,0 58 | Scalettar 1982,5640,0,0,8585,0,0,3.08,0,0,0,0 59 | Scalettar 1982,5640,0,0,9398,0,0,3.18,0,0,0,0 60 | Scalettar 1982,5640,0,0,9790,0,0,3.29,0,0,0,0 61 | Scalettar 1982,5640,0,0,9873,0,0,3.21,0,0,0,0 62 | Hitachi 1987,5305,0,0,376,0,0,0.6113559322,0,0,0,0 63 | Hitachi 1987,5305,0,0,887,0,0,0.9544915254,0,0,0,0 64 | Hitachi 1987,5305,0,0,2248.87,0,0,1.754652542,0,0,0,0 65 | Hitachi 1987,5305,0,0,2793.61,0,0,1.982826271,0,0,0,0 66 | Hitachi 1987,5305,0,0,3780.87,0,0,2.362881356,0,0,0,0 67 | Hitachi 1987,5305,0,0,6606.15,0,0,3.312,0,0,0,0 68 | Hitachi 1987,5305,0,0,9499.19,0,0,4.146059322,0,0,0,0 69 | Hitachi 1987,5305,0,0,14332.4,0,0,5.587161017,0,0,0,0 70 | Hitachi 1987,5305,0,0,18144.2,0,0,6.610042373,0,0,0,0 71 | Hitachi 1987,5305,0,0,21921.5,0,0,7.403262712,0,0,0,0 72 | Hitachi 1987,5305,0,0,23861.7,0,0,8.010338983,0,0,0,0 73 | Hitachi 1987,6120,0,0,1329.56,0,0,1.336110169,0,0,0,0 74 | Hitachi 1987,6120,0,0,4666.86,0,0,3.087834746,0,0,0,0 75 | Hitachi 1987,6120,0,0,9466.83,0,0,4.873644068,0,0,0,0 76 | Hitachi 1987,6120,0,0,14129.9,0,0,6.276949153,0,0,0,0 77 | Hitachi 1987,6120,0,0,18928.7,0,0,7.565,0,0,0,0 78 | Hitachi 1987,6120,0,0,23557,0,0,8.662118644,0,0,0,0 79 | Hitachi 1987,6120,0,0,28287,0,0,9.567457627,0,0,0,0 80 | Hitachi 1987,6120,0,0,33084.6,0,0,10.31949153,0,0,0,0 81 | Hitachi 1987,6120,0,0,37780.3,0,0,11.11008475,0,0,0,0 -------------------------------------------------------------------------------- /include/NEST/LArParameters.hh: -------------------------------------------------------------------------------- 1 | /** 2 | * @file LArParameters.hh 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @author Justin Mueller [Justin.Mueller@colostate.edu] 6 | * @author Ekaterina Kozlova [aspelene@gmail.com] 7 | * @author Michael Mooney [mrmooney@colostate.edu] 8 | * @brief 9 | * @version 10 | * @date 2022-04-13 11 | */ 12 | #pragma once 13 | #include 14 | 15 | #include "RandomGen.hh" 16 | 17 | namespace NEST { 18 | static constexpr double LAr_Z{18}; 19 | static constexpr double legacy_density_LAr{1.393}; 20 | static constexpr double legacy_scint_yield{1.0 / (19.5 * 1.e-6)}; 21 | static constexpr double legacy_resolution_scale{0.107}; // Doke 1976 22 | static constexpr double two_PI = 2. * M_PI; 23 | static constexpr double sqrt2 = gcem::sqrt(2.); 24 | static constexpr double sqrt2_PI = gcem::sqrt(2. * M_PI); 25 | static constexpr double inv_sqrt2_PI = 1. / gcem::sqrt(2. * M_PI); 26 | 27 | enum class LArInteraction { 28 | NR = 0, 29 | ER = 1, 30 | Alpha = 2, 31 | dEdx = 3, 32 | LeptonLET = 4, 33 | LET = 5 34 | }; 35 | 36 | enum class LArFluctuationModel { 37 | Default = 0, 38 | }; 39 | 40 | struct LArNRYieldsParameters { 41 | double alpha = {11.10}; 42 | double beta = {0.087}; 43 | double gamma = {0.1}; 44 | double delta = {-0.0932}; 45 | double epsilon = {2.998}; 46 | double zeta = {0.3}; 47 | double eta = {2.94}; 48 | }; 49 | struct LArERElectronYieldsAlphaParameters { 50 | double A = {32.988}; 51 | double B = {-552.988}; 52 | double C = {17.2346}; 53 | double D = {-4.7}; 54 | double E = {0.025115}; 55 | double F = {0.265360653}; 56 | double G = {0.242671}; 57 | }; 58 | struct LArERElectronYieldsBetaParameters { 59 | double A = {0.778482}; 60 | double B = {25.9}; 61 | double C = {1.105}; 62 | double D = {0.4}; 63 | double E = {4.55}; 64 | double F = {-7.502}; 65 | }; 66 | struct LArERElectronYieldsGammaParameters { 67 | double A = {0.659509}; 68 | double B = {1000}; 69 | double C = {6.5}; 70 | double D = {5.0}; 71 | double E = {-0.5}; 72 | double F = {1047.408}; 73 | double G = {0.01851}; 74 | }; 75 | struct LArERElectronYieldsDokeBirksParameters { 76 | double A = {1052.264}; 77 | double B = {14159350000 - 1652.264}; 78 | double C = {-5.0}; 79 | double D = {0.157933}; 80 | double E = {1.83894}; 81 | }; 82 | struct LArERYieldsParameters { 83 | LArERElectronYieldsAlphaParameters alpha; 84 | LArERElectronYieldsBetaParameters beta; 85 | LArERElectronYieldsGammaParameters gamma; 86 | LArERElectronYieldsDokeBirksParameters doke_birks; 87 | double p1 = {1.0}; 88 | double p2 = {10.304}; 89 | double p3 = {13.0654}; 90 | double p4 = {0.10535}; 91 | double p5 = {0.7}; 92 | double delta = {15.7489}; 93 | double let = {-2.07763}; 94 | }; 95 | 96 | struct LArAlphaElectronYieldsParameters { 97 | double A = {1.0 / 6200.0}; 98 | double B = {64478398.7663}; 99 | double C = {0.173553719}; 100 | double D = {1.21}; 101 | double E = {0.02852}; 102 | double F = {0.01}; 103 | double G = {4.71598}; 104 | double H = {7.72848}; 105 | double I = {-0.109802}; 106 | double J = {3.0}; 107 | }; 108 | 109 | struct LArAlphaPhotonYieldsParameters { 110 | double A = {1.16}; 111 | double B = {-0.012}; 112 | double C = {1.0 / 6500.0}; 113 | double D = {278037.250283}; 114 | double E = {0.173553719}; 115 | double F = {1.21}; 116 | double G = {2}; 117 | double H = {0.653503}; 118 | double I = {4.98483}; 119 | double J = {10.0822}; 120 | double K = {1.2076}; 121 | double L = {-0.97977}; 122 | double M = {3.0}; 123 | }; 124 | 125 | struct LArAlphaYieldsParameters { 126 | LArAlphaElectronYieldsParameters Ye; 127 | LArAlphaPhotonYieldsParameters Yph; 128 | }; 129 | 130 | struct ThomasImelParameters { 131 | double A = {0.1}; 132 | double B = {-0.0932}; 133 | }; 134 | 135 | struct DriftParameters { 136 | std::vector A = {0.937729, 0.80302379, 0.7795972, 0.6911897, 137 | 0.76551511, 0.502022794, 0.24207633}; 138 | std::vector B = {-0.0734108, -0.06694564, -0.0990952, -0.092997, 139 | -0.0731659, -0.06644517, -0.03558428}; 140 | std::vector C = {0.315338, 0.331798, 0.320876, 0.3295202, 141 | 0.317972, 0.3290246, 0.33645519}; 142 | 143 | std::vector TempLow = {84., 86., 88., 92., 96., 110., 125.}; 144 | std::vector TempHigh = {86., 88., 92., 96., 110., 125., 140.}; 145 | }; 146 | } // namespace NEST -------------------------------------------------------------------------------- /examples/LArNEST/LegacyLArNESTBenchmarks.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file legacyLArNEST.cpp 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "LArDetector.hh" 16 | #include "LArNEST.hh" 17 | 18 | int main(int argc, char* argv[]) { 19 | // this sample program takes several arguments 20 | // including 21 | // 1) num_events - the number of events to generate 22 | // 2) pdgcode - the pdg code of the particle creating the deposition 23 | // 3) density - the density of the LAr 24 | // 4) track_length - the size of the track length for the deposition 25 | // (optional) 5) seed - a seed for the random number generator 26 | if (argc < 5) { 27 | std::cerr << "Error! This program expects six inputs!" << std::endl; 28 | exit(0); 29 | } 30 | size_t num_events = atoi(argv[1]); 31 | 32 | // set up energy steps 33 | int num_energy_steps = 50000; 34 | std::vector energy_vals; 35 | double start_val = .1; 36 | double end_val = 1000; 37 | double step_size = (end_val - start_val) / num_energy_steps; 38 | for (size_t i = 0; i < num_energy_steps; i++) { 39 | energy_vals.emplace_back(start_val + step_size * i); 40 | } 41 | 42 | int pdgcode = atoi(argv[2]); 43 | double density = atof(argv[3]); 44 | std::vector electric_field = {1, 50, 100, 200, 500, 1000, 1500, 2000}; 45 | 46 | double track_length = atof(argv[4]); 47 | uint64_t seed = 0; 48 | if (argc > 5) { 49 | seed = atoi(argv[5]); 50 | } 51 | 52 | LArDetector* detector = new LArDetector(); 53 | // Construct NEST class using detector object 54 | NEST::LArNEST larnest(detector); 55 | // Set random seed of RandomGen class 56 | RandomGen::rndm()->SetSeed(seed); 57 | 58 | // Construct NEST objects for storing calculation results 59 | NEST::LArYieldResult result; 60 | std::ofstream output_file; 61 | output_file.open("legacy_benchmarks_" + std::to_string(pdgcode) + ".csv"); 62 | output_file 63 | << "energy,efield,TotalYield,QuantaYield,LightYield,Nph,Ne,Nex,Nion\n"; 64 | // iterate over number of events 65 | for (size_t v = 0; v < electric_field.size(); v++) { 66 | for (size_t i = 0; i < num_energy_steps; i++) { 67 | std::vector TotalYield(num_events); 68 | std::vector QuantaYield(num_events); 69 | std::vector LightYield(num_events); 70 | std::vector Nph(num_events); 71 | std::vector Ne(num_events); 72 | std::vector Nex(num_events); 73 | std::vector Nion(num_events); 74 | // collect statistics for each event 75 | for (size_t j = 0; j < num_events; j++) { 76 | result = larnest.LegacyCalculation( 77 | pdgcode, energy_vals[i], electric_field[v], density, track_length); 78 | TotalYield[j] = result.TotalYield; 79 | QuantaYield[j] = result.QuantaYield; 80 | LightYield[j] = result.LightYield; 81 | Nph[j] = result.Nph; 82 | Ne[j] = result.Ne; 83 | Nex[j] = result.Nex; 84 | Nion[j] = result.Nion; 85 | } 86 | double TotalYield_mean = 87 | std::accumulate(TotalYield.begin(), TotalYield.end(), 0.0) / 88 | double(num_events); 89 | double QuantaYield_mean = 90 | std::accumulate(QuantaYield.begin(), QuantaYield.end(), 0.0) / 91 | double(num_events); 92 | double LightYield_mean = 93 | std::accumulate(LightYield.begin(), LightYield.end(), 0.0) / 94 | double(num_events); 95 | double Nph_mean = 96 | std::accumulate(Nph.begin(), Nph.end(), 0.0) / double(num_events); 97 | double Ne_mean = 98 | std::accumulate(Ne.begin(), Ne.end(), 0.0) / double(num_events); 99 | double Nex_mean = 100 | std::accumulate(Nex.begin(), Nex.end(), 0.0) / double(num_events); 101 | double Nion_mean = 102 | std::accumulate(Nion.begin(), Nion.end(), 0.0) / double(num_events); 103 | output_file << energy_vals[i] << ","; 104 | output_file << electric_field[v] << ","; 105 | output_file << TotalYield_mean << "," << QuantaYield_mean << "," 106 | << LightYield_mean << ","; 107 | output_file << Nph_mean << "," << Ne_mean << "," << Nex_mean << "," 108 | << Nion_mean << "\n"; 109 | } 110 | } 111 | output_file.close(); 112 | return 0; 113 | } -------------------------------------------------------------------------------- /include/Detectors/DS.hh: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DS_hh 3 | #define DS_hh 1 4 | 5 | #include "VDetector.hh" 6 | using namespace std; 7 | 8 | 9 | 10 | class DS_Detector : public VDetector { 11 | public: 12 | DS_Detector() { 13 | Initialization(); 14 | }; 15 | ~DS_Detector() override = default; 16 | 17 | 18 | void Initialization() override { 19 | name = "DS-50"; 20 | g1 = 0.16; 21 | sPEres = 0.05; // Personal communication with Shawn 22 | sPEthr = 0.6; // 23 | sPEeff = 1; //I take 1 just for the sake of lowering free parameters 24 | noiseBaseline[0] = 0.00; 25 | noiseBaseline[1] = 0.00; 26 | noiseBaseline[2] = 0.; 27 | noiseBaseline[3] = 0.; 28 | P_dphe = 0.02; 29 | 30 | //also from arXiv:2311.18647 31 | coinWind = 100; 32 | coinLevel = 2; 33 | numPMTs = 38; 34 | 35 | OldW13eV = true; 36 | noiseLinear[0] = 0; 37 | noiseLinear[1] = 0; 38 | 39 | // Ionization and Secondary Scintillation (S2) parameters 40 | //these parameters are incorrect - S2 still in pre-alpha mode 41 | g1_gas = 0.1; 42 | s2Fano = 3.61; 43 | s2_thr = 300.; 44 | //these parameters are incorrect - S2 still in pre-alpha mode 45 | 46 | E_gas = 4.2; 47 | eLife_us = 10000.; 48 | 49 | // Thermodynamic Properties 50 | //right now they're incorrect and just used for reference taking into account the LAr drift velocities haven't been updated yet (you can uncomment them manually, but) 51 | T_Kelvin = 89.; 52 | p_bar = 1.2; 53 | 54 | // Data Analysis Parameters and Geometry 55 | dtCntr = 177.5;//a bit random 56 | dt_min = 20; //a bit random 57 | dt_max = 375.; //calculated from arXiv:2311.18647 58 | 59 | //from arXiv:2311.18647 60 | radius = 230.; 61 | radmax = 230.; 62 | 63 | //arXiv:1410.0653 64 | TopDrift = 306; 65 | anode = 316; 66 | gate = 256; 67 | cathode = 0; 68 | 69 | // 2-D (X & Y) Position Reconstruction - still from LUX 70 | PosResExp = 0.015; 71 | PosResBase = 70.8364; 72 | } 73 | 74 | double FitS1(double xPos_mm, double yPos_mm, double zPos_mm, 75 | LCE map) override { 76 | return 1.0; 77 | } 78 | 79 | // Drift electric field as function of Z in mm 80 | double FitEF(double xPos_mm, double yPos_mm, 81 | double zPos_mm) override { // in V/cm 82 | return 200.; 83 | } 84 | 85 | double FitS2(double xPos_mm, double yPos_mm, LCE map) override { return 1.0; } 86 | 87 | vector FitTBA(double xPos_mm, double yPos_mm, 88 | double zPos_mm) override { 89 | vector BotTotRat(2); 90 | 91 | BotTotRat[0] = 0.6; // S1 bottom-to-total ratio 92 | BotTotRat[1] = 0.323; // S2 bottom-to-total ratio, typically only used for 93 | // position recon (1-this) 94 | 95 | return BotTotRat; 96 | } 97 | 98 | //very preliminary opttrans function for LAr 99 | double OptTrans(double xPos_mm, double yPos_mm, double zPos_mm) override { 100 | double phoTravT, approxCenter = (TopDrift + cathode) / 2., 101 | relativeZ = zPos_mm - approxCenter; 102 | return - 1.001*pow(abs(relativeZ),1.6142)+1.0008*pow(abs(relativeZ),1.6142)+5.04080; 103 | } 104 | //unchanged from LUX 105 | vector SinglePEWaveForm(double area, double t0) override { 106 | vector PEperBin; 107 | 108 | double threshold = PULSEHEIGHT; // photo-electrons 109 | double sigma = PULSE_WIDTH; // ns 110 | area *= 10. * (1. + threshold); 111 | double amplitude = area / (sigma * sqrt(2. * M_PI)), 112 | signal; // assumes perfect Gaussian 113 | 114 | double tStep1 = SAMPLE_SIZE / 1e2; // ns, make sure much smaller than 115 | // sample size; used to generate MC-true 116 | // pulses essentially 117 | double tStep2 = 118 | SAMPLE_SIZE; // ns; 1 over digitization rate, 100 MHz assumed here 119 | 120 | double time = -5. * sigma; 121 | bool digitizeMe = false; 122 | while (true) { 123 | signal = amplitude * exp(-pow(time, 2.) / (2. * sigma * sigma)); 124 | if (signal < threshold) { 125 | if (digitizeMe) 126 | break; 127 | else 128 | ; // do nothing - goes down to advancing time block 129 | } else { 130 | if (digitizeMe) 131 | PEperBin.push_back(signal); 132 | else { 133 | if (RandomGen::rndm()->rand_uniform() < 2. * (tStep1 / tStep2)) { 134 | PEperBin.push_back(time + t0); 135 | PEperBin.push_back(signal); 136 | digitizeMe = true; 137 | } else { 138 | } 139 | } 140 | } 141 | if (digitizeMe) 142 | time += tStep2; 143 | else 144 | time += tStep1; 145 | if (time > 5. * sigma) break; 146 | } 147 | 148 | return PEperBin; 149 | } 150 | }; 151 | 152 | #endif 153 | -------------------------------------------------------------------------------- /utils/larnest/parameters.py: -------------------------------------------------------------------------------- 1 | """ 2 | Collections of datasets and information. 3 | """ 4 | nr_total_yield_default = { 5 | 100: { 6 | "SCENE 2015": [96.4, 193, 293], 7 | "ARIS 2018": [200], 8 | }, 9 | } 10 | nr_charge_yield_default = { 11 | 100: { 12 | "SCENE 2015": [96.4], 13 | }, 14 | 250: { 15 | "SCENE 2015": [193, 293], 16 | "ARIS 2018": [200], 17 | "Joshi 2014": [240], 18 | }, 19 | 500: { 20 | "SCENE 2015": [486], 21 | "Joshi 2014": [640], 22 | }, 23 | 1750: { 24 | "Joshi 2014": [1600, 2130], 25 | }, 26 | } 27 | nr_light_yield_default = { 28 | 1: { 29 | "ARIS 2018": [0], 30 | "SCENE 2015": [0], 31 | "Regenfus 2012":[0], 32 | "WARP 2005": [0], 33 | "CREUS 2015": [0], 34 | "MicroClean 2012": [0], 35 | }, 36 | 50: { 37 | "SCENE 2013": [50], 38 | "ARIS 2018": [50], 39 | }, 40 | 100: { 41 | "SCENE 2015": [100], 42 | "ARIS 2018": [100], 43 | }, 44 | 250: { 45 | "SCENE 2015": [193, 293], 46 | "ARIS 2018": [200], 47 | "SCENE 2013": [300], 48 | }, 49 | 500: { 50 | "ARIS 2018": [500], 51 | }, 52 | } 53 | er_charge_yield_default = { 54 | 1: { 55 | "ARIS 2018": [0], 56 | "DarkSide10 2013": [0], 57 | "Lippincott 2010": [0], 58 | "WARP 2005": [0], 59 | "Kimura 2019": [0], 60 | "Doke 2002": [0] 61 | }, 62 | 100: { 63 | "ARIS 2018": [50,100], 64 | "Scalettar 1982": [84,89,94,101,110,119,128,129,130,139,145,148], 65 | }, 66 | 200: { 67 | "ARIS 2018": [200], 68 | "Joshi 2014": [200], 69 | "Scalettar 1982":[152,160,176,180,185,188,192,200,205,210,240,243,248,268,270,276,285,309,313,351,371,375,388,391] 70 | }, 71 | 600: { 72 | "ARIS 2018": [500], 73 | "Joshi 2014": [550], 74 | "Scalettar 1982": [410,411,427,441,478,481,490,510,531,536,601,620,626,661], 75 | "Bondar 2016": [600], 76 | #"Aprile ": [572] 77 | }, 78 | 1000: { 79 | "Joshi 2014": [1200], 80 | "Scalettar 1982": [801,820,832,899,904,943], 81 | #"Aprile ": [] 82 | }, 83 | 1500: { 84 | "Joshi 2014": [1600,1750], 85 | "Scalettar 1982": [1004,1012,1064,1244,1403,1455], 86 | "Bondar 2016": [1750], 87 | }, 88 | 2500: { 89 | "Joshi 2014": [2150,2400,3000], 90 | "Scalettar 1982":[2009,2064,2806,2913], 91 | "Bondar 2016": [2400], 92 | "Sangiorgio": [2400], 93 | "Doke 2002": [2010], 94 | }, 95 | 6000: { 96 | "Scalettar 1982":[4600,5704,6586,6693], 97 | "Doke 2002": [4020,5000,6010], 98 | #"Aprile", 99 | }, 100 | 9500: { 101 | "Scalettar 1982":[8490,8673,9481,9681], 102 | "Doke 2002": [8000,9000,9990], 103 | #"Aprile", 104 | }, 105 | } 106 | er_light_yield_default = { 107 | 1: { 108 | "ARIS 2018": [0], 109 | "DarkSide10 2013": [0], 110 | "Lippincott 2010": [0], 111 | "WARP 2005": [0], 112 | "Kimura 2019": [0], 113 | "Kimura LIDINE":[0], 114 | }, 115 | 100: { 116 | "ARIS 2018": [50,100], 117 | "Scalettar 1982": [84,89,94,101,110,119,128,129,130,139,145,148], 118 | }, 119 | 200: { 120 | "ARIS 2018": [200], 121 | "Joshi 2014": [200], 122 | "Scalettar 1982":[152,160,176,180,185,188,192,200,205,210,240,243,248,268,270,276,285,309,313,351,371,375,388,391] 123 | }, 124 | 600: { 125 | "ARIS 2018": [500], 126 | "Joshi 2014": [550], 127 | "Scalettar 1982": [410,411,427,441,478,481,490,510,531,536,601,620,626,661], 128 | "Bondar 2016": [600], 129 | #"Aprile ": [572] 130 | }, 131 | 1000: { 132 | "Joshi 2014": [1200], 133 | "Scalettar 1982": [801,820,832,899,904,943], 134 | #"Aprile ": [] 135 | }, 136 | 1500: { 137 | "Joshi 2014": [1600,1750], 138 | "Scalettar 1982": [1004,1012,1064,1244,1403,1455], 139 | "Bondar 2016": [1750], 140 | }, 141 | 2500: { 142 | "Joshi 2014": [2150,2400,3000], 143 | "Scalettar 1982":[2009,2064,2806,2913], 144 | "Bondar 2016": [2400], 145 | "Sangiorgio": [2400], 146 | "Doke 2002": [2010], 147 | }, 148 | 6000: { 149 | "Scalettar 1982":[4600,5704,6586,6693], 150 | "Doke 2002": [4020,5000,6010], 151 | #"Aprile", 152 | }, 153 | 9500: { 154 | "Scalettar 1982":[8490,8673,9481,9681], 155 | "Doke 2002": [8000,9000,9990], 156 | #"Aprile", 157 | }, 158 | } 159 | 160 | default_plot_config = { 161 | "nr_total": nr_total_yield_default, 162 | "nr_charge": nr_charge_yield_default, 163 | "nr_light": nr_light_yield_default, 164 | "er_charge": er_charge_yield_default, 165 | "er_light": er_light_yield_default, 166 | } -------------------------------------------------------------------------------- /examples/LUXRun03_betaEff_Simulated.txt: -------------------------------------------------------------------------------- 1 | E_true S1 Mean S1 Res [%] S2 Mean S2 Res [%] Ec Mean Ec Res[%] Eff[%>thr] 2 | 0.16 0.002376 0 0 nan nan nan 0.0000 3 | 0.17 1.93516 nan 262.910697 nan 0.524715 nan 0.0001 4 | 0.18 1.7997 nan 224.550269 nan 0.465997 nan 0.0001 5 | 0.19 2.289593 0.774557 212.387122 40.077517 0.5121 17.892618 0.0002 6 | 0.2 2.143304 3.271948 207.6423 17.672209 0.489103 6.539892 0.0003 7 | 0.22 1.860675 8.097215 233.970729 14.819419 0.483783 9.776808 0.0008 8 | 0.24 1.991705 10.091829 239.286243 15.740471 0.505554 4.221187 0.0008 9 | 0.26 1.973749 8.260653 263.357372 18.307276 0.529894 9.602664 0.001 10 | 0.28 1.959789 12.329374 248.925704 23.610549 0.512298 12.297289 0.0014 11 | 0.3 1.907531 13.13291 285.803944 23.028798 0.546582 15.087861 0.0028 12 | 0.32 1.956436 10.050371 294.418843 22.442232 0.562014 13.652078 0.0041 13 | 0.34 1.955854 9.535712 311.955892 24.460587 0.581265 14.223086 0.0041 14 | 0.36 2.047731 9.741738 331.860907 23.05653 0.614355 13.791709 0.0062 15 | 0.38 1.985184 11.468614 343.601214 23.340985 0.619693 15.326167 0.0075 16 | 0.4 1.977992 11.678518 354.015286 24.17712 0.630294 15.732192 0.0084 17 | 0.42 1.995479 14.636798 386.918132 23.421953 0.668669 16.017625 0.0106 18 | 0.44 2.020361 12.179821 388.496268 23.173506 0.67343 15.209665 0.0121 19 | 0.46 1.955962 10.712453 400.116028 24.14263 0.67841 16.006066 0.0183 20 | 0.48 1.963885 11.93024 420.521639 21.327099 0.701855 14.702391 0.0185 21 | 0.5 1.9706 11.476202 429.944856 22.657822 0.713053 15.678696 0.0196 22 | 0.55 1.923954 12.876236 474.032949 23.051597 0.755962 16.449045 0.0422 23 | 0.6 1.961949 13.208855 516.550547 21.252898 0.807421 15.566802 0.4689 24 | 0.65 2.002721 15.066975 554.875698 20.709555 0.854599 15.429647 1.5553 25 | 0.7 2.052612 17.934385 587.868877 20.350521 0.89701 15.485166 3.3974 26 | 0.75 2.099183 20.405796 622.507057 19.91439 0.940829 15.47649 6.1193 27 | 0.8 2.149506 22.681705 654.472018 19.470648 0.982159 15.409605 9.6729 28 | 0.85 2.207118 24.779015 685.998251 19.144502 1.023891 15.410266 14.0092 29 | 0.9 2.273223 26.956023 715.969627 18.847439 1.064942 15.441473 19.1043 30 | 0.95 2.342404 28.846823 745.411956 18.534372 1.105783 15.426435 24.6479 31 | 1 2.42049 30.706384 774.032226 18.285714 1.146799 15.430369 30.6073 32 | 1.1 2.596592 33.960846 827.819186 17.830604 1.227448 15.549567 42.8642 33 | 1.2 2.806529 36.634799 878.734487 17.457609 1.309042 15.646024 54.7417 34 | 1.3 3.054234 38.820024 927.095087 17.179479 1.392408 15.798172 65.4048 35 | 1.4 3.326895 40.346291 972.554224 16.905739 1.475607 15.957299 74.1391 36 | 1.5 3.640391 41.313455 1016.084678 16.721471 1.56164 16.105334 81.1944 37 | 1.6 3.984252 41.78587 1057.792629 16.5492 1.649352 16.199811 86.6402 38 | 1.7 4.361777 41.703184 1098.099843 16.44051 1.739607 16.248965 90.5447 39 | 1.8 4.771713 41.333379 1137.074036 16.303515 1.832327 16.263807 93.354 40 | 1.9 5.213664 40.586835 1174.490539 16.236051 1.927218 16.202101 95.3092 41 | 2 5.667818 39.704822 1210.937961 16.19864 2.02252 16.118479 96.5815 42 | 2.2 6.639026 37.813865 1280.959262 16.141453 2.217586 15.912372 98.071 43 | 2.4 7.647133 35.940967 1348.424224 16.130494 2.414301 15.620053 98.8557 44 | 2.6 8.688943 34.362607 1413.703469 16.162649 2.612682 15.388563 99.3599 45 | 2.8 9.749362 32.825399 1476.744195 16.201375 2.810835 15.081626 99.6362 46 | 3 10.833424 31.475736 1539.197224 16.268738 3.011187 14.82089 99.8031 47 | 3.2 11.925328 30.241638 1599.514167 16.291733 3.21011 14.521128 99.8911 48 | 3.4 13.032032 29.117926 1659.07604 16.345176 3.409968 14.244675 99.9438 49 | 3.6 14.147493 28.056311 1717.769722 16.398897 3.609901 13.97687 99.9672 50 | 3.8 15.285644 27.080002 1775.717834 16.423734 3.811729 13.680694 99.9828 51 | 4 16.420369 26.238103 1832.434128 16.449958 4.011745 13.448659 99.9897 52 | 4.2 17.553939 25.440265 1889.755539 16.488653 4.212251 13.196145 99.9945 53 | 4.4 18.701473 24.732021 1945.32225 16.499621 4.412471 12.988455 99.9974 54 | 4.6 19.851198 24.085633 2001.411505 16.453055 4.613489 12.761861 99.9986 55 | 4.8 21.003333 23.414566 2056.722114 16.467625 4.813895 12.538122 99.9991 56 | 5 22.152799 22.849321 2111.427561 16.451148 5.013261 12.353072 99.9996 57 | 5.5 25.075896 21.574693 2245.860166 16.41553 5.514861 11.895895 99.9999 58 | 6 27.9917 20.491902 2380.324684 16.315891 6.015265 11.490553 100 59 | 6.5 30.923287 19.558586 2513.272075 16.225826 6.515532 11.122899 100 60 | 7 33.872794 18.716154 2644.512344 16.120639 7.015679 10.767138 100 61 | 7.5 36.830038 17.987797 2774.33919 16.026663 7.514786 10.474981 100 62 | 8 39.773936 17.359926 2904.524876 15.905064 8.01225 10.191768 100 63 | 8.5 42.746832 16.765343 3032.557071 15.813443 8.510395 9.922135 100 64 | 9 45.73176 16.238576 3160.481307 15.670377 9.009422 9.673804 100 65 | 9.5 48.702702 15.75421 3288.956858 15.577534 9.506935 9.455814 100 66 | 10 51.682724 15.336981 3416.587539 15.470548 10.004139 9.251229 100 67 | 11 57.645404 14.57298 3671.764892 15.263622 10.997477 8.882961 100 68 | 12 63.623496 13.920495 3924.238299 15.063415 11.987987 8.544467 100 69 | 13 69.587896 13.337676 4178.365537 14.88734 12.977043 8.24753 100 70 | 14 75.559241 12.826365 4431.075428 14.692955 13.963926 7.977468 100 71 | 15 81.531428 12.403167 4685.887883 14.531396 14.951824 7.746013 100 72 | 16 87.501253 12.040337 4939.259849 14.397108 15.936589 7.538134 100 73 | 17 93.472118 11.760625 5195.396736 14.261503 16.923489 7.378648 100 74 | 18 99.518177 11.675956 5450.737132 14.120035 17.917498 7.321283 100 75 | 19 105.697283 11.787182 5710.085639 14.00547 18.930906 7.38993 100 76 | 20 112.057555 11.983467 5969.01331 13.905275 19.964818 7.502703 100.000 77 | -------------------------------------------------------------------------------- /src/GammaHandler.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "GammaHandler.hh" 3 | #include 4 | #include "TestSpectra.hh" 5 | 6 | using namespace std; 7 | 8 | double yMax = 1.0; // arbitrary y max, might need to change 9 | double brThresh = 0.1; 10 | 11 | const vector>& GammaHandler::sourceLookupTable( 12 | const std::string& source) { 13 | // energy container vector orginized as {energy, branch ratio, PE mass 14 | // attenuation coef, Compton coef, Pair Production coef} 15 | typedef vector> LookupTable; 16 | static const LookupTable co57Info{ 17 | {122.0, 0.856, 1.793, 0.1081, 0.00}, 18 | {136.0, 0.1068, 0.5651, 0.1019, 0.00}, 19 | {14.0, 0.0916, 55.5, 0.0744, 0.00}, 20 | }; 21 | static const LookupTable co60Info{ 22 | {1332.0, 0.9998, 0.001991, 0.04244, 0.0008853}, 23 | {1173.0, 0.9985, 0.004126, 0.05156, 0.00}, 24 | }; 25 | static const LookupTable cs137Info{ 26 | {662.0, 0.851, 0.01338, 0.06559, 0.00}, 27 | {284.0, 0.0006, 0.08009, 0.08495, 0.00}, 28 | }; 29 | if (source == "Co57") { 30 | return co57Info; 31 | } else if (source == "Co60") { 32 | return co60Info; 33 | } else if (source == "Cs137") { 34 | return cs137Info; 35 | } 36 | cerr << source << " Is not a valid option!" << endl; 37 | throw std::invalid_argument{source + " is not a valid option!"}; 38 | return co57Info; 39 | } 40 | 41 | const vector GammaHandler::combineSpectra(double emin, double emax, 42 | string source) { 43 | double brSum = 0.0; 44 | double fValue = 0.0; 45 | double pe = 0.0; 46 | double compton = 0.0; 47 | double pp = 0.0; 48 | 49 | vector> sourceInfo = GammaHandler::sourceLookupTable(source); 50 | 51 | vector xyTry = { 52 | emin + (emax - emin) * RandomGen::rndm()->rand_uniform(), 53 | yMax * RandomGen::rndm()->rand_uniform(), 1.}; 54 | 55 | while (xyTry[2] > 0.) { 56 | pe = GammaHandler::photoIonization(sourceInfo, xyTry); 57 | compton = GammaHandler::compton(sourceInfo, xyTry); 58 | pp = GammaHandler::pairProduction(sourceInfo, xyTry); 59 | fValue = pe + compton + pp; 60 | xyTry = RandomGen::rndm()->VonNeumann(emin, emax, 0., yMax, xyTry[0], 61 | xyTry[1], fValue); 62 | } 63 | 64 | vector keV_vec = {-1, -1, -1}; 65 | 66 | if (pe > 0.0) { 67 | keV_vec[0] = xyTry[0]; 68 | } 69 | if (compton > 0.0) { 70 | keV_vec[1] = xyTry[0]; 71 | } 72 | if (pp > 0.0) { 73 | keV_vec[2] = xyTry[0]; 74 | } 75 | return keV_vec; 76 | } 77 | 78 | double GammaHandler::photoIonization(const vector>& sourceInfo, 79 | const vector& xyTry) { 80 | // implement simple delta function to the spectrum 81 | std::size_t index{0}; 82 | bool found = false; 83 | for (int i = 0; i < sourceInfo.size(); i++) { 84 | double initialEnergy = sourceInfo[i][0]; 85 | if (abs(xyTry[0] - initialEnergy) < brThresh) { 86 | index = i; 87 | found = true; 88 | } 89 | } 90 | double br = sourceInfo[index][1]; 91 | double pe = sourceInfo[index][2]; 92 | double co = sourceInfo[index][3]; 93 | double pp = sourceInfo[index][4]; 94 | if (found) return yMax * br * (pe / (pe + co + pp)); 95 | return 0.0; 96 | } 97 | 98 | double GammaHandler::compton(const vector>& sourceInfo, 99 | const vector& xyTry) { 100 | double energyScaleFactor = 101 | ElectronRestMassEnergy; // mc^2 for electron mass in keV 102 | double thetaMin = 0.0; 103 | double thetaMax = M_PI; 104 | int simpIterations = 100; 105 | double simpStep = (thetaMax - thetaMin) / simpIterations; 106 | double simpCurrentStep = thetaMin; 107 | double shiftedEnergy, simpResult, kn, B, rY, rPsi, initialEnergy; 108 | bool draw = true; 109 | double a = 1.0 / 137.04; 110 | double re = pow(0.38616, -12); 111 | 112 | // loop over gamma energies 113 | for (int i = 0; i < sourceInfo.size(); i++) { 114 | double initialEnergy = sourceInfo[i][0]; 115 | double br = sourceInfo[i][1]; 116 | double pe = sourceInfo[i][2]; 117 | double co = sourceInfo[i][3]; 118 | double pp = sourceInfo[i][4]; 119 | // get shifted energy with MC 120 | bool draw = true; 121 | while (draw) { 122 | rPsi = M_PI * RandomGen::rndm()->rand_uniform(); 123 | rY = 10 * RandomGen::rndm()->rand_uniform(); 124 | 125 | B = 1.0 / (1.0 + initialEnergy / energyScaleFactor * (1 - cos(rPsi))); 126 | kn = M_PI * pow(B, 2) * (B + 1.0 / B - pow(sin(rPsi), 2)) * 127 | sin(rPsi); // Klein-Nishina 128 | if (rY < kn) draw = false; 129 | } 130 | shiftedEnergy = 131 | initialEnergy * 132 | (1.0 - 1.0 / (1.0 + initialEnergy / energyScaleFactor * 133 | (1.0 - cos(rPsi)))); // shifted energy formula 134 | if (abs(xyTry[0] - shiftedEnergy) < brThresh) { 135 | return kn * yMax * br * (co / (pe + co + pp)); 136 | } 137 | } 138 | return 0.0; 139 | } 140 | 141 | double GammaHandler::pairProduction(const vector>& sourceInfo, 142 | const vector& xyTry) { 143 | double energyScaleFactor = 144 | ElectronRestMassEnergy; // mc^2 for electron mass in keV 145 | double initialEnergy, shiftedEnergy; 146 | // loop over allowed gamma energies 147 | for (int i = 0; i < sourceInfo.size(); i++) { 148 | double initialEnergy = sourceInfo[i][0]; 149 | double br = sourceInfo[i][1]; 150 | double pe = sourceInfo[i][2]; 151 | double co = sourceInfo[i][3]; 152 | double pp = sourceInfo[i][4]; 153 | shiftedEnergy = (0.5) * (initialEnergy - 154 | 2 * energyScaleFactor); // Pair production energy 155 | if (abs(xyTry[0] - shiftedEnergy) < brThresh) { 156 | return yMax * br * (pp / (pe + co + pp)); 157 | } 158 | } 159 | return 0.0; 160 | } 161 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # Set minimum CMake version required and prevent building in source 3 | cmake_minimum_required(VERSION 3.8...3.16 FATAL_ERROR) 4 | 5 | ############### Get version from git ##################### 6 | execute_process(COMMAND git describe --tag 7 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 8 | OUTPUT_VARIABLE PROJECT_GIT_VERSION 9 | OUTPUT_STRIP_TRAILING_WHITESPACE) 10 | 11 | function(get_versions versionString version) 12 | if ("${versionString}" STREQUAL "") 13 | set(version "0.0.0" PARENT_SCOPE) 14 | return() 15 | endif () 16 | 17 | string(REGEX REPLACE "v([0-9]*)([.][0-9]*[.][0-9]*-?.*)$" "\\1" numbers ${versionString}) 18 | set(major ${numbers}) 19 | string(REGEX REPLACE "v([0-9]*[.])([0-9]*)([.][0-9]*-?.*)$" "\\2" numbers ${versionString}) 20 | set(minor ${numbers}) 21 | string(REGEX REPLACE "v([0-9]*[.][0-9]*[.])([0-9]*)(-?.*)$" "\\2" numbers ${versionString}) 22 | set(patch ${numbers}) 23 | set(version "${major}.${minor}.${patch}" PARENT_SCOPE) 24 | endfunction() 25 | 26 | get_versions("${PROJECT_GIT_VERSION}" version) 27 | set(PROJECT_GIT_VERSION ${version}) 28 | 29 | ############### project name and version ##################### 30 | project(NEST VERSION ${PROJECT_GIT_VERSION} LANGUAGES CXX) 31 | 32 | option(G4 "Build integration with Geant4" OFF) 33 | option(BUILD_ROOT "Build ROOT tools" OFF) 34 | option(BUILD_EXAMPLES "Build NEST examples" ON) 35 | option(BUILD_GARFIELD "Build integration with Garfield++" ON) 36 | 37 | # enable position independent code so shared libraries can link against 38 | # the static version of NEST::Core 39 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 40 | 41 | include(GNUInstallDirs) 42 | include(CMakePackageConfigHelpers) 43 | include(cmake/gcem.cmake) 44 | 45 | 46 | 47 | set(NEST_CORE_SOURCES 48 | ${CMAKE_CURRENT_SOURCE_DIR}/src/NEST.cpp 49 | ${CMAKE_CURRENT_SOURCE_DIR}/src/RandomGen.cpp 50 | ${CMAKE_CURRENT_SOURCE_DIR}/src/TestSpectra.cpp 51 | ${CMAKE_CURRENT_SOURCE_DIR}/src/VDetector.cpp 52 | ${CMAKE_CURRENT_SOURCE_DIR}/src/GammaHandler.cpp 53 | ${CMAKE_CURRENT_SOURCE_DIR}/src/ValidityTests.cpp 54 | ${CMAKE_CURRENT_SOURCE_DIR}/src/LArNEST.cpp 55 | ${CMAKE_CURRENT_SOURCE_DIR}/src/LArDetector.cpp 56 | ${CMAKE_CURRENT_SOURCE_DIR}/src/TestLArSpectra.cpp 57 | ) 58 | set(NEST_CORE_HEADERS 59 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/NEST.hh 60 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/RandomGen.hh 61 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/TestSpectra.hh 62 | ${CMAKE_CURRENT_SOURCE_DIR}/include/Detectors/VDetector.hh 63 | ${CMAKE_CURRENT_SOURCE_DIR}/include/Detectors/LArDetector.hh 64 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/GammaHandler.hh 65 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/ValidityTests.hh 66 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/LArNEST.hh 67 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/TestLArSpectra.hh 68 | ) 69 | 70 | add_library(NESTCore ${NEST_CORE_SOURCES} ${NEST_CORE_HEADERS}) 71 | 72 | target_link_libraries(NESTCore PUBLIC gcem) 73 | 74 | list(APPEND NEST_TARGETS "NESTCore") 75 | 76 | target_include_directories( 77 | NESTCore PUBLIC 78 | $ 79 | $ 80 | $ 81 | $ 82 | $ 83 | $ 84 | ) 85 | 86 | target_compile_features(NESTCore PUBLIC cxx_std_17) 87 | 88 | add_executable(execNEST 89 | ${CMAKE_CURRENT_SOURCE_DIR}/src/execNEST.cpp 90 | ${CMAKE_CURRENT_SOURCE_DIR}/include/NEST/execNEST.hh 91 | ) 92 | target_link_libraries(execNEST PUBLIC NESTCore) 93 | list(APPEND NEST_TARGETS "execNEST") 94 | 95 | ## scripts 96 | configure_file(examples/loopNEST.in loopNEST) 97 | configure_file(examples/220RnCalib.in 220RnCalib) 98 | 99 | ## Optional targets 100 | 101 | if(BUILD_EXAMPLES) 102 | add_subdirectory(examples) 103 | endif() 104 | 105 | if(BUILD_GARFIELD) 106 | add_subdirectory(GarfieldppIntegration) 107 | endif() 108 | 109 | if(G4) 110 | add_subdirectory(G4integration) 111 | list(APPEND NEST_TARGETS "NESTG4") 112 | endif() 113 | 114 | # installation, Targets.cmake and Config.cmake 115 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/NEST) 116 | 117 | write_basic_package_version_file( 118 | NESTConfigVersion.cmake 119 | VERSION ${PACKAGE_VERSION} 120 | COMPATIBILITY ExactVersion 121 | ) 122 | 123 | configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake/NESTConfig.cmake.in 124 | ${CMAKE_CURRENT_BINARY_DIR}/NESTConfig.cmake 125 | INSTALL_DESTINATION ${INSTALL_CONFIGDIR} 126 | ) 127 | 128 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 129 | 130 | install(FILES 131 | ${CMAKE_CURRENT_BINARY_DIR}/NESTConfig.cmake 132 | ${CMAKE_CURRENT_BINARY_DIR}/NESTConfigVersion.cmake 133 | DESTINATION ${INSTALL_CONFIGDIR} 134 | ) 135 | 136 | message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_LIBDIR}") 137 | message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_BINDIR}") 138 | message(STATUS "CMAKE_INSTALL_LIBDIR: ${CMAKE_INSTALL_INCLUDEDIR}") 139 | install( 140 | TARGETS ${NEST_TARGETS} 141 | EXPORT NEST-Targets 142 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 143 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 144 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 145 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 146 | ) 147 | 148 | install( 149 | EXPORT NEST-Targets 150 | FILE NESTTargets.cmake 151 | NAMESPACE NEST:: 152 | DESTINATION ${INSTALL_CONFIGDIR} 153 | ) 154 | 155 | ## install scripts 156 | 157 | install(FILES 158 | ${CMAKE_CURRENT_BINARY_DIR}/loopNEST 159 | ${CMAKE_CURRENT_BINARY_DIR}/220RnCalib 160 | PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE 161 | DESTINATION ${CMAKE_INSTALL_BINDIR} 162 | ) 163 | 164 | ############################################## 165 | 166 | export( 167 | EXPORT NEST-Targets 168 | FILE ${CMAKE_CURRENT_BINARY_DIR}/NESTTargets.cmake 169 | NAMESPACE NEST:: 170 | ) 171 | 172 | # Register package in user's package registry 173 | set(CMAKE_EXPORT_PACKAGE_REGISTRY ON) 174 | export(PACKAGE NEST) 175 | -------------------------------------------------------------------------------- /src/RandomGen.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "RandomGen.hh" 3 | #include 4 | 5 | using namespace std; 6 | 7 | // Global static pointer used to ensure a single instance of the class. 8 | RandomGen* RandomGen::m_pInstance = nullptr; 9 | 10 | // Only allow one instance of class to be generated. 11 | RandomGen* RandomGen::rndm() { 12 | if (!m_pInstance) m_pInstance = new RandomGen; 13 | return m_pInstance; 14 | } 15 | 16 | std::uint64_t splitmix64(std::uint64_t z) { 17 | z += 0x9e3779b97f4a7c15; 18 | z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; 19 | z = (z ^ (z >> 27)) * 0x94d049bb133111eb; 20 | return z ^ (z >> 31); 21 | } 22 | 23 | void RandomGen::SetSeed(uint64_t s) { 24 | if (rng_locked) { 25 | throw std::runtime_error("You can not change the seed because it is locked."); 26 | } 27 | uint64_t s1 = splitmix64(s); 28 | rng = xoroshiro128plus64(s1, splitmix64(s1)); 29 | } 30 | 31 | void RandomGen::LockSeed() { 32 | rng_locked = true; 33 | } 34 | 35 | void RandomGen::UnlockSeed() { 36 | rng_locked = false; 37 | } 38 | 39 | double RandomGen::rand_uniform() { 40 | return (static_cast(rng()) - xoroshiro128plus64_min) / 41 | xoroshiro128plus64_minmax; 42 | } 43 | 44 | double RandomGen::rand_gauss(double mean, double sigma, bool zero_min) { 45 | // std::normal_distribution norm(mean, sigma); 46 | // return norm(rng); 47 | double u = rand_uniform(), v = rand_uniform(); 48 | double draw = mean + sigma * sqrt2 * sqrt(-log(u)) * cos(two_PI * v); 49 | if (zero_min) { 50 | return max(draw, 0.); 51 | } 52 | return draw; 53 | } 54 | 55 | double RandomGen::rand_zero_trunc_gauss(double mean, double sigma) { 56 | double r = rand_gauss(mean, sigma, false); 57 | while (r <= 0) { 58 | r = rand_gauss(mean, sigma, false); 59 | } 60 | return r; 61 | } 62 | 63 | double RandomGen::FindNewMean(double sigma) { 64 | // Follow https://en.wikipedia.org/wiki/Truncated_normal_distribution 65 | double TruncGaussAlpha = -1. / sigma; 66 | double LittlePhi_Alpha = 67 | exp(-0.5 * TruncGaussAlpha * TruncGaussAlpha) / gcem::sqrt(2. * M_PI); 68 | double BigPhi_Alpha = 0.5 * (1. + erf(TruncGaussAlpha / sqrt2)); 69 | return 1. + (LittlePhi_Alpha / (1. - BigPhi_Alpha)) * sigma; 70 | } 71 | 72 | double RandomGen::rand_exponential(double half_life, double t_min, 73 | double t_max) { 74 | double r = rand_uniform(); 75 | double r_min = 0; 76 | double r_max = 1; 77 | if (t_min >= 0) { 78 | r_min = 1 - exp(-t_min * log2 / half_life); 79 | } 80 | if (t_max >= 0) { 81 | r_max = 1 - exp(-t_max * log2 / half_life); 82 | } 83 | r = (r_max - r_min) * r + r_min; 84 | return -log(1 - r) * half_life / log2; 85 | } 86 | 87 | double RandomGen::rand_skewGauss(double xi, double omega, double alpha) { 88 | double delta = alpha / sqrt(1 + alpha * alpha); 89 | double gamma1 = four_minus_PI_div_2 * 90 | (pow(delta * sqrt2_div_PI, 3.) / 91 | pow(1 - 2. * delta * delta / M_PI, 1.5)); // skewness 92 | double muz = delta * sqrt2_div_PI; 93 | double sigz = sqrt(1. - muz * muz); 94 | double m_o; 95 | if (alpha > 0.) { 96 | m_o = muz - 0.5 * gamma1 * sigz - 0.5 * exp(-two_PI / alpha); 97 | } 98 | if (alpha < 0.) { 99 | m_o = muz - 0.5 * gamma1 * sigz + 0.5 * exp(two_PI / alpha); 100 | } 101 | double mode = xi + omega * m_o; 102 | // the height should be the value of the PDF at the mode 103 | double height = exp(-0.5 * (pow((mode - xi) / omega, 2.))) / 104 | (sqrt2_PI * omega) * 105 | erfc(-1. * alpha * (mode - xi) / omega / sqrt2); 106 | bool gotValue = false; 107 | double minX = xi - 6. * omega; 108 | double maxX = 109 | xi + 6. * omega; // +/- 6sigma should be essentially +/- infinity 110 | // can increase these for even better accuracy, at the 111 | // cost of speed 112 | double testX, testY, testProb; 113 | while (gotValue == false) { 114 | testX = minX + (maxX - minX) * RandomGen::rndm()->rand_uniform(); 115 | testY = height * 116 | RandomGen::rndm()->rand_uniform(); // between 0 and peak height 117 | // calculate the value of the skewGauss PDF at the test x-value 118 | testProb = exp(-0.5 * (pow((testX - xi) / omega, 2.))) / 119 | (sqrt2_PI * omega) * 120 | erfc(-1. * alpha * (testX - xi) / omega / sqrt2); 121 | if (testProb >= testY) { 122 | gotValue = true; 123 | } 124 | } 125 | return testX; 126 | } 127 | 128 | uint64_t RandomGen::poisson_draw(double mean) { 129 | return std::poisson_distribution(mean)(rng); 130 | } 131 | 132 | int64_t RandomGen::binom_draw(int64_t N0, double prob) { 133 | return std::binomial_distribution(N0, prob)(rng); 134 | } 135 | 136 | int RandomGen::integer_range(int min, int max) { 137 | return std::uniform_int_distribution(min, max)(rng); 138 | } 139 | 140 | vector RandomGen::VonNeumann(double xMin, double xMax, double yMin, 141 | double yMax, double xTest, double yTest, 142 | double fValue) { 143 | vector xyTry(3); 144 | 145 | xyTry[0] = xTest; 146 | xyTry[1] = yTest; 147 | 148 | if (xyTry[1] > fValue) { 149 | xyTry[0] = xMin + (xMax - xMin) * RandomGen::rndm()->rand_uniform(); 150 | xyTry[1] = yMin + (yMax - yMin) * RandomGen::rndm()->rand_uniform(); 151 | xyTry[2] = 1.; 152 | } else 153 | xyTry[2] = 0.; 154 | 155 | return xyTry; // doing a vector means you can return 2 values at the same 156 | // time 157 | } 158 | 159 | int RandomGen::SelectRanXeAtom() { // to determine the isotope of Xe 160 | int A; 161 | double isotope = rand_uniform() * 100.; 162 | 163 | if (isotope > 0.000 && isotope <= 0.090) 164 | A = 124; 165 | else if (isotope > 0.090 && isotope <= 0.180) 166 | A = 126; 167 | else if (isotope > 0.180 && isotope <= 2.100) 168 | A = 128; 169 | else if (isotope > 2.100 && isotope <= 28.54) 170 | A = 129; 171 | else if (isotope > 28.54 && isotope <= 32.62) 172 | A = 130; 173 | else if (isotope > 32.62 && isotope <= 53.80) 174 | A = 131; 175 | else if (isotope > 53.80 && isotope <= 80.69) 176 | A = 132; 177 | else if (isotope > 80.69 && isotope <= 91.13) 178 | A = 134; 179 | else 180 | A = 136; 181 | return A; 182 | } 183 | -------------------------------------------------------------------------------- /examples/LUXRun03_CH3TBand_SpikeFull.txt: -------------------------------------------------------------------------------- 1 | 2.0000 1.5000 2.6644 0.0050000 0.123100 0.0030000 2 | 3.0000 2.5000 2.5623 0.0050000 0.120240 0.0030000 3 | 4.0000 3.5000 2.4598 0.0050000 0.106700 0.0030000 4 | 5.0000 4.5000 2.3884 0.0050000 0.101030 0.0030000 5 | 6.0000 5.5000 2.3286 0.0050000 0.104000 0.0030000 6 | 7.0000 6.5000 2.2847 0.0050000 0.103570 0.0030000 7 | 8.0000 7.5000 2.2475 0.0050000 0.100720 0.0030000 8 | 9.0000 8.5000 2.2114 0.0050000 0.099107 0.0030000 9 | 10.000 9.5000 2.1798 0.0050000 0.096387 0.0030000 10 | 11.000 10.500 2.1560 0.0050000 0.096337 0.0030000 11 | 12.000 11.500 2.1330 0.0050000 0.097562 0.0030000 12 | 13.000 12.500 2.1113 0.0050000 0.10037 0.0030000 13 | 14.000 13.500 2.0910 0.0050000 0.10014 0.0030000 14 | 15.000 14.500 2.0736 0.0050000 0.099924 0.0030000 15 | 16.000 15.500 2.0599 0.0050000 0.10193 0.0030000 16 | 17.000 16.500 2.0425 0.0050000 0.10553 0.0030000 17 | 18.000 17.500 2.0297 0.0050000 0.10517 0.0030000 18 | 19.000 18.500 2.0149 0.0050000 0.10148 0.0030000 19 | 20.000 19.500 2.0022 0.0050000 0.10425 0.0030000 20 | 21.000 20.500 1.9935 0.0050000 0.10148 0.0030000 21 | 22.000 21.500 1.9793 0.0050000 0.098926 0.0030000 22 | 23.000 22.500 1.9673 0.0050000 0.10317 0.0030000 23 | 24.000 23.500 1.9628 0.0050000 0.10119 0.0030000 24 | 25.000 24.500 1.9547 0.0050000 0.099190 0.0030000 25 | 26.000 25.500 1.9472 0.0050000 0.10396 0.0030000 26 | 27.000 26.500 1.9391 0.0050000 0.10746 0.0030000 27 | 28.000 27.500 1.9306 0.0050000 0.10148 0.0030000 28 | 29.000 28.500 1.9269 0.0050000 0.096045 0.0030000 29 | 30.000 29.500 1.9201 0.0050000 0.097950 0.0030000 30 | 31.000 30.500 1.9118 0.0050000 0.10012 0.0030000 31 | 32.000 31.500 1.9028 0.0050000 0.10203 0.0030000 32 | 33.000 32.500 1.8976 0.0050000 0.10162 0.0030000 33 | 34.000 33.500 1.8919 0.0050000 0.10097 0.0030000 34 | 35.000 34.500 1.8849 0.0050000 0.098612 0.0030000 35 | 36.000 35.500 1.8808 0.0050000 0.10141 0.0030000 36 | 37.000 36.500 1.8786 0.0050000 0.10554 0.0030000 37 | 38.000 37.500 1.8738 0.0050000 0.094737 0.0030000 38 | 39.000 38.500 1.8684 0.0050000 0.091920 0.0030000 39 | 40.000 39.500 1.8630 0.0050000 0.097350 0.0030000 40 | 41.000 40.500 1.8533 0.0050000 0.097032 0.0030000 41 | 42.000 41.500 1.8529 0.0050000 0.096535 0.0030000 42 | 43.000 42.500 1.8531 0.0050000 0.10020 0.0030000 43 | 44.000 43.500 1.8452 0.0050000 0.098506 0.0030000 44 | 45.000 44.500 1.8391 0.0050000 0.090486 0.0030000 45 | 46.000 45.500 1.8372 0.0050000 0.094319 0.0030000 46 | 47.000 46.500 1.8343 0.0050000 0.095868 0.0030000 47 | 48.000 47.500 1.8289 0.0050000 0.092815 0.0030000 48 | 49.000 48.500 1.8262 0.0050000 0.095902 0.0030000 49 | 50.000 49.500 1.8218 0.0050000 0.093164 0.0030000 50 | 51.000 50.500 1.8137 0.0050000 0.092112 0.0030000 51 | 52.000 51.500 1.8114 0.0050000 0.092489 0.0030000 52 | 53.000 52.500 1.8110 0.0050000 0.094256 0.0030000 53 | 54.000 53.500 1.8031 0.0050000 0.096090 0.0030000 54 | 55.000 54.500 1.7991 0.0050000 0.090629 0.0030000 55 | 56.000 55.500 1.7989 0.0050000 0.092276 0.0030000 56 | 57.000 56.500 1.7983 0.0050000 0.091731 0.0030000 57 | 58.000 57.500 1.7952 0.0050000 0.091111 0.0030000 58 | 59.000 58.500 1.7933 0.0050000 0.093824 0.0030000 59 | 60.000 59.500 1.7870 0.0050000 0.094203 0.0030000 60 | 61.000 60.500 1.7804 0.0050000 0.094764 0.0030000 61 | 62.000 61.500 1.7798 0.0050000 0.092295 0.0030000 62 | 63.000 62.500 1.7758 0.0050000 0.086048 0.0030000 63 | 64.000 63.500 1.7758 0.0050000 0.088368 0.0030000 64 | 65.000 64.500 1.7720 0.0050000 0.085427 0.0030000 65 | 66.000 65.500 1.7656 0.0050000 0.085729 0.0030000 66 | 67.000 66.500 1.7625 0.0050000 0.090161 0.0030000 67 | 68.000 67.500 1.7618 0.0050000 0.085331 0.0030000 68 | 69.000 68.500 1.7607 0.0050000 0.085984 0.0030000 69 | 70.000 69.500 1.7521 0.0050000 0.081960 0.0030000 70 | 71.000 70.500 1.7474 0.0050000 0.081573 0.0030000 71 | 72.000 71.500 1.7438 0.0050000 0.088042 0.0030000 72 | 73.000 72.500 1.7420 0.0050000 0.083571 0.0030000 73 | 74.000 73.500 1.7440 0.0050000 0.083496 0.0030000 74 | 75.000 74.500 1.7402 0.0050000 0.083660 0.0030000 75 | 76.000 75.500 1.7354 0.0050000 0.079120 0.0030000 76 | 77.000 76.500 1.7284 0.0050000 0.084555 0.0030000 77 | 78.000 77.500 1.7318 0.0050000 0.090668 0.0030000 78 | 79.000 78.500 1.7273 0.0050000 0.097174 0.0030000 79 | 80.000 79.500 1.7217 0.0050000 0.094683 0.0030000 80 | 81.000 80.500 1.7188 0.0050000 0.088476 0.0030000 81 | 82.000 81.500 1.7175 0.0050000 0.082237 0.0030000 82 | 83.000 82.500 1.7202 0.0050000 0.075279 0.0030000 83 | 84.000 83.500 1.7084 0.0050000 0.075097 0.0030000 84 | 85.000 84.500 1.7001 0.0050000 0.083490 0.0030000 85 | 86.000 85.500 1.7005 0.0050000 0.085101 0.0030000 86 | 87.000 86.500 1.6968 0.0050000 0.083401 0.0030000 87 | 88.000 87.500 1.6879 0.0050000 0.083341 0.0030000 88 | 89.000 88.500 1.6878 0.0050000 0.073531 0.0030000 89 | 90.000 89.500 1.6955 0.0050000 0.065899 0.0030000 90 | 91.000 90.500 1.6902 0.0050000 0.070637 0.0030000 91 | 92.000 91.500 1.6855 0.0050000 0.075278 0.0030000 92 | 93.000 92.500 1.6865 0.0050000 0.070851 0.0030000 93 | 94.000 93.500 1.6702 0.0050000 0.079263 0.0030000 94 | 95.000 94.500 1.6694 0.0050000 0.085277 0.0030000 95 | 96.000 95.500 1.6736 0.0050000 0.077838 0.0030000 96 | 97.000 96.500 1.6540 0.0050000 0.071516 0.0030000 97 | 98.000 97.500 1.6414 0.0050000 0.071845 0.0030000 98 | 99.000 98.500 1.6451 0.0050000 0.078833 0.0030000 99 | 100.00 99.500 1.6309 0.0100000 0.085000 0.0060000 100 | 101.00 100.50 1.6370 0.0100000 0.085000 0.0060000 101 | 102.00 101.50 1.6409 0.0100000 0.085000 0.0060000 102 | 103.00 102.50 1.6457 0.0100000 0.085000 0.0060000 103 | 104.00 103.50 1.6730 0.0100000 0.085000 0.0060000 104 | 105.00 104.50 1.6764 0.0100000 0.085000 0.0060000 105 | 106.00 105.50 1.6695 0.0100000 0.085000 0.0060000 106 | 107.00 106.50 1.6581 0.0100000 0.085000 0.0060000 107 | 108.00 107.50 1.6422 0.0100000 0.085000 0.0060000 108 | 109.00 108.50 1.6616 0.0100000 0.085000 0.0060000 109 | 110.00 109.50 1.6673 0.0100000 0.085000 0.0060000 110 | 111.00 110.50 1.6468 0.0100000 0.085000 0.0060000 111 | 112.00 111.50 1.6217 0.0100000 0.085000 0.0060000 112 | 113.00 112.50 1.6320 0.0100000 0.085000 0.0060000 113 | 114.00 113.50 1.6320 0.0100000 0.085000 0.0060000 114 | 115.00 114.50 1.6240 0.0100000 0.085000 0.0060000 115 | 116.00 115.50 1.6092 0.0100000 0.085000 0.0060000 116 | 117.00 116.50 1.6115 0.0100000 0.085000 0.0060000 117 | 118.00 117.50 1.6958 0.0100000 0.085000 0.0060000 118 | 119.00 118.50 1.6537 0.0100000 0.085000 0.0060000 119 | 120.00 119.50 1.6366 0.0100000 0.085000 0.0060000 120 | -------------------------------------------------------------------------------- /examples/LUXRun03_DDBand_SpikeFull.txt: -------------------------------------------------------------------------------- 1 | 2.25 2.25 2.1739 0.0055 0.1621 0.01357 2 | 3.35 3.35 2.0574 0.0057 0.1748 0.01206 3 | 4.45 4.45 1.9841 0.0056 0.1608 0.01041 4 | 5.55 5.55 1.9361 0.0063 0.168 0.01471 5 | 6.65 6.65 1.9232 0.0066 0.1634 0.00975 6 | 7.75 7.75 1.8965 0.0067 0.1567 0.01008 7 | 8.85 8.85 1.873 0.0067 0.1436 0.01123 8 | 9.95 9.95 1.8443 0.007 0.1345 0.00974 9 | 11.05 11.05 1.8415 0.0069 0.1336 0.01253 10 | 12.15 12.15 1.8295 0.0073 0.1197 0.0072 11 | 13.25 13.25 1.7932 0.0077 0.1301 0.00543 12 | 14.35 14.35 1.7907 0.0072 0.1224 0.00469 13 | 15.45 15.45 1.7839 0.0077 0.1149 0.00884 14 | 16.55 16.55 1.7637 0.0078 0.1148 0.01263 15 | 17.65 17.65 1.761 0.0083 0.1184 0.01308 16 | 18.75 18.75 1.7381 0.0085 0.1104 0.0035 17 | 19.85 19.85 1.7475 0.0085 0.1092 0.00612 18 | 20.95 20.95 1.7252 0.0082 0.1084 0.00758 19 | 22.05 22.05 1.7003 0.009 0.1143 0.00593 20 | 23.15 23.15 1.6928 0.0097 0.1042 0.01084 21 | 24.25 24.25 1.7058 0.0098 0.1138 0.01738 22 | 25.35 25.35 1.689 0.0126 0.1323 0.00519 23 | 26.45 26.45 1.6689 0.0112 0.1143 0.00795 24 | 27.55 27.55 1.6774 0.0131 0.1252 0.01083 25 | 28.65 28.65 1.6438 0.0094 0.089 0.01232 26 | 29.75 29.75 1.6524 0.0118 0.1041 0.0068 27 | 30.85 30.85 1.6273 0.0105 0.1009 0.00819 28 | 31.95 31.95 1.6457 0.009 0.0893 0.00789 29 | 33.05 33.05 1.6462 0.0106 0.0957 0.0055 30 | 34.15 34.15 1.6159 0.0118 0.0997 0.00489 31 | 35.25 35.25 1.6131 0.0102 0.074 0.00804 32 | 36.35 36.35 1.593 0.0105 0.0815 0.00987 33 | 37.45 37.45 1.6087 0.0122 0.0996 0.0159 34 | 38.55 38.55 1.6002 0.0119 0.0943 0.00556 35 | 39.65 39.65 1.5972 0.0118 0.0927 0.01196 36 | 40.75 40.75 1.5766 0.013 0.1068 0.01109 37 | 41.85 41.85 1.5729 0.0107 0.0763 0.0146 38 | 42.95 42.95 1.5742 0.0136 0.1018 0.00993 39 | 44.05 44.05 1.5972 0.0138 0.0888 0.00976 40 | 45.15 45.15 1.5586 0.0164 0.1066 0.00942 41 | 46.25 46.25 1.5804 0.0116 0.09 0.01497 42 | 47.35 47.35 1.5572 0.0099 0.0625 0.00696 43 | 48.45 48.45 1.5484 0.0173 0.107 0.00395 44 | 49.55 49.55 1.559 0.0127 0.0917 0.0212 45 | 50.65 50.65 1.5491 0.0117 0.0769 0.01802 46 | 51.75 51.75 1.5358 0.0128 0.087 0.01029 47 | 52.85 52.85 1.5329 0.013 0.0729 0.01216 48 | 53.95 53.95 1.5272 0.0124 0.0813 0.01716 49 | 55.05 55.05 1.5106 0.0138 0.0813 0.01142 50 | 56.15 56 1.5095 0.00943 0.06164 0.00947 51 | 57.25 57 1.5308 0.00511 0.07937 0.00502 52 | 58.35 58 1.5152 0.005 0.0914 0.007 53 | 59.45 59 1.5018 0.00777 0.06943 0.00831 54 | 60.55 61 1.4928 0.00755 0.04882 0.00784 55 | 61.65 62 1.5054 0.01043 0.08399 0.01099 56 | 62.75 63 1.4629 0.00896 0.05967 0.01056 57 | 63.85 64 1.4843 0.00893 0.05203 0.00889 58 | 64.95 65 1.4762 0.009 0.08 0.009 59 | 66.05 66 1.5092 0.01734 0.0771 0.01746 60 | 67.15 67 1.4914 0.00306 0.07144 0.00308 61 | 68.25 68 1.4797 0.01619 0.08446 0.0164 62 | 69.35 69 1.5206 .013037 0.08 0.015363 63 | 70.45 70 1.48 .018937 0.08 0.007266 64 | 71.55 72 1.4605 0.00341 0.06129 0.00341 65 | 72.65 73 1.4691 0.00692 0.05 0.00587 66 | 73.75 74 1.4427 0.00613 0.05 0.00641 67 | 74.85 75 1.4734 0.02258 0.07342 0.02274 68 | 75.95 76 1.4443 0.01311 0.09313 0.01338 69 | 77.05 77 1.4508 0.012 0.09107 0.01257 70 | 78.15 78 1.4184 0.00685 0.07053 0.0074 71 | 79.25 79 1.4216 0.00492 0.06829 0.00491 72 | 80.35 80 1.3923 0.01908 0.10146 0.02151 73 | 81.45 81 1.4376 0.01009 0.05485 0.01013 74 | 82.55 83 1.4234 0.00813 0.05281 0.00816 75 | 83.65 84 1.3983 0.01346 0.09312 0.01469 76 | 84.75 85 1.4264 0.00557 0.05949 0.00577 77 | 85.85 86 1.4423 0.01218 0.04978 0.01208 78 | 86.95 87 1.3719 0.02368 0.10919 0.02227 79 | 88.05 88 1.389 0.02221 0.08932 0.02449 80 | 89.15 89 1.395 0.01474 0.08 0.00746 81 | 90.25 90 1.411 0.00702 0.05169 0.00707 82 | 91.35 91 1.3875 0.00284 0.07551 0.00288 83 | 92.45 92 1.3943 0.02025 0.09077 0.02255 84 | 93.55 94 1.3613 0.01279 0.06805 0.01353 85 | 94.65 95 1.3825 0.003 0.04522 0.003 86 | 95.75 96 1.3862 0.00834 0.04593 0.00843 87 | 96.85 97 1.3709 0.00573 0.07336 0.00643 88 | 97.95 98 1.376 0.00594 0.04417 0.00593 89 | 99.05 99 1.3517 0.00558 0.08883 0.00636 90 | 100.15 100 1.362 0.02729 0.08303 0.02867 91 | 101.25 101 1.3595 0.0114 0.06362 0.01248 92 | 102.35 102 1.376 0.00569 0.04192 0.0059 93 | 103.45 103 1.3435 0.0026 0.04745 0.00272 94 | 104.55 105 1.3515 0.00693 0.05171 0.00733 95 | 105.65 106 1.3313 0.01181 0.04332 0.01199 96 | 106.75 107 1.3504 0.00596 0.0468 0.00598 97 | 107.85 108 1.3589 0.01542 0.07181 0.01583 98 | 108.95 109 1.339 0.00903 0.06787 0.00944 99 | 110.05 110 1.357 0.01874 0.04183 0.01298 100 | 111.15 112 1.3685 .010285 .073267 0.0100 101 | 112.25 112 1.3575 .009996 .070333 0.01000 102 | 113.35 112 1.3465 .009750 .0674 0.01000 103 | 114.45 115 1.3355 .009548 .064467 0.01000 104 | 115.55 115 1.33275 .009389 .06795 0.01000 105 | 116.65 117 1.33825 .009273 .07785 0.01000 106 | 117.75 117 1.3325 .0092 .0775 0.01000 107 | 118.85 120 1.3215 .009170 .072367 0.01000 108 | 119.95 120 1.3105 .009184 .067233 0.01000 109 | 121.05 122 1.331 .009241 .084325 0.01000 110 | 122.15 122 1.348 .009340 .0992 0.01000 111 | 123.25 122 1.33333 .009484 .093333 0.01000 112 | 124.35 125 1.31867 .00967 .087467 0.01000 113 | 125.45 125 1.31 .009899 .081975 0.01000 114 | 126.55 127 1.31 .010172 .077025 0.01000 115 | 127.65 127 1.30783 .010488 .075433 0.01000 116 | 128.75 130 1.30417 .010847 .076167 0.01000 117 | 129.85 130 1.3005 .011249 .0769 0.01000 118 | 130.95 130 1.3 .011695 .077633 0.01000 119 | 132.05 133 1.3 .012184 .078367 0.01000 120 | 133.15 133 1.30075 .012715 .0796 0.01000 121 | 134.25 133 1.30625 .013291 .084 0.01000 122 | 135.35 137 1.31175 .013909 .0884 0.01000 123 | 136.45 137 1.31725 .014570 .0928 0.01000 124 | 137.55 137 1.3145 .015275 .09115 0.01000 125 | 138.65 137 1.3035 .016023 .08345 0.01000 126 | 139.75 141 1.2925 .016814 .07575 0.01000 127 | 140.85 141 1.2815 .017648 .06805 0.01000 128 | 141.95 141 1.28633 .018525 .073808 0.01000 129 | 143.05 141 1.29367 .019446 .081692 0.01000 130 | 144.15 147 1.301 .020410 .089575 0.01000 131 | 145.25 147 1.30833 .021417 .097458 0.01000 132 | 146.35 147 1.31567 .022467 .105342 0.01000 133 | 147.45 147 1.343 .023560 .120563 0.01000 134 | 148.55 147 1.29337 .024697 .101938 0.01000 135 | 149.65 147 1.34256 .025877 .123313 0.01000 136 | 150.75 147 1.28821 .027100 .104688 0.01000 137 | 151.85 155 1.3423 .028366 .126063 0.01000 138 | 152.95 155 1.28289 .029675 .107438 0.01000 139 | 154.05 155 1.34222 .031028 .128813 0.01000 140 | 155.15 155 1.27739 .032424 .110188 0.01000 141 | 156.25 155 1.3423 .033863 .131563 0.01000 142 | 157.35 155 1.27172 .035345 .112938 0.01000 143 | 158.45 155 1.34256 .036870 .134313 0.01000 144 | 159.55 155 1.2659 .038438 .11569 0.01000 145 | -------------------------------------------------------------------------------- /examples/LArNEST/LArNESTFluctuationBenchmarks.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file legacyLArNEST.cpp 3 | * @author NEST Collaboration 4 | * @author Nicholas Carrara [nmcarrara@ucdavis.edu] 5 | * @brief Benchmarks for LAr ER model. This program generates ... 6 | * @version 7 | * @date 2022-04-14 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "LArDetector.hh" 16 | #include "LArNEST.hh" 17 | 18 | int main(int argc, char* argv[]) { 19 | // this sample program takes can take two arguments, 20 | // (optional) 1) density - the density of the LAr to use 21 | // (optional) 2) seed - a seed for the random number generator 22 | 23 | double density = 1.393; 24 | uint64_t seed = 0; 25 | uint64_t num_events = 100; 26 | if (argc > 1) { 27 | density = atof(argv[1]); 28 | } 29 | if (argc > 2) { 30 | seed = atoi(argv[2]); 31 | } 32 | if (argc > 3) { 33 | num_events = atoi(argv[3]); 34 | } 35 | 36 | // set up energy steps 37 | int num_energy_steps = 50000; 38 | std::vector energy_vals; 39 | double start_val = .1; 40 | double end_val = 1000; 41 | double step_size = (end_val - start_val) / num_energy_steps; 42 | 43 | for (size_t i = 0; i < num_energy_steps; i++) { 44 | energy_vals.emplace_back(start_val + step_size * i); 45 | } 46 | std::vector particle_types = { 47 | NEST::LArInteraction::NR, NEST::LArInteraction::ER, 48 | NEST::LArInteraction::Alpha}; 49 | std::vector particle_type = {"NR", "ER", "Alpha"}; 50 | 51 | LArDetector* detector = new LArDetector(); 52 | // Construct NEST class using detector object 53 | NEST::LArNEST larnest(detector); 54 | // Set random seed of RandomGen class 55 | RandomGen::rndm()->SetSeed(seed); 56 | 57 | // Construct NEST objects for storing calculation results 58 | NEST::LArNESTResult result; 59 | std::ofstream output_file; 60 | 61 | output_file.open("fluctuation_benchmarks.csv"); 62 | output_file << "type,energy,efield,TotalYield,QuantaYield,LightYield,Nph,Ne," 63 | "Nex,Nion,TotalYield_std,QuantaYield_std,LightYield_std,Nph_" 64 | "std,Ne_std,Nex_std,Nion_std\n"; 65 | 66 | // iterate over electric field values 67 | for (size_t k = 0; k < particle_types.size(); k++) { 68 | std::vector electric_field; 69 | if (particle_types[k] == NEST::LArInteraction::NR) { 70 | electric_field.insert(electric_field.end(), {1, 50, 100, 200, 250, 500, 71 | 1000, 1500, 1750, 2000}); 72 | } else if (particle_types[k] == NEST::LArInteraction::ER) { 73 | electric_field.insert(electric_field.end(), {1, 100, 200, 600, 1000, 1500, 74 | 2500, 6000, 9000, 9500}); 75 | } else { 76 | electric_field.insert(electric_field.end(), 77 | {1, 50, 100, 500, 1000, 5000, 10000, 20000}); 78 | } 79 | // iterate over number of events 80 | for (size_t v = 0; v < electric_field.size(); v++) { 81 | for (size_t i = 0; i < num_energy_steps; i++) { 82 | std::vector Nph(num_events); 83 | std::vector Ne(num_events); 84 | std::vector Nex(num_events); 85 | std::vector Nion(num_events); 86 | // collect statistics for each event 87 | for (size_t j = 0; j < num_events; j++) { 88 | result = larnest.FullCalculation(particle_types[k], energy_vals[i], 0, 89 | electric_field[v], density, false); 90 | Nph[j] = double(result.fluctuations.NphFluctuation); 91 | Ne[j] = double(result.fluctuations.NeFluctuation); 92 | Nex[j] = double(result.fluctuations.NexFluctuation); 93 | Nion[j] = double(result.fluctuations.NionFluctuation); 94 | } 95 | double Nph_mean = 96 | std::accumulate(Nph.begin(), Nph.end(), 0.0) / double(num_events); 97 | double Ne_mean = 98 | std::accumulate(Ne.begin(), Ne.end(), 0.0) / double(num_events); 99 | double Nex_mean = 100 | std::accumulate(Nex.begin(), Nex.end(), 0.0) / double(num_events); 101 | double Nion_mean = 102 | std::accumulate(Nion.begin(), Nion.end(), 0.0) / double(num_events); 103 | 104 | double Nq_mean = Nph_mean + Ne_mean; 105 | double Nq_std = 0.0; 106 | double Nph_std = 0.0; 107 | double Ne_std = 0.0; 108 | double Nex_std = 0.0; 109 | double Nion_std = 0.0; 110 | 111 | for (size_t ii = 0; ii < Nph.size(); ii++) { 112 | Nq_std += (Nph[ii] + Ne[ii] - Nq_mean) * (Nph[ii] + Ne[ii] - Nq_mean); 113 | } 114 | Nq_std = std::sqrt(Nq_std / double(num_events)); 115 | 116 | for (size_t ii = 0; ii < Nph.size(); ii++) { 117 | Nph_std += (Nph[ii] - Nph_mean) * (Nph[ii] - Nph_mean); 118 | } 119 | Nph_std = std::sqrt(Nph_std / double(num_events)); 120 | 121 | for (size_t ii = 0; ii < Ne.size(); ii++) { 122 | Ne_std += (Ne[ii] - Ne_mean) * (Ne[ii] - Ne_mean); 123 | } 124 | Ne_std = std::sqrt(Ne_std / double(num_events)); 125 | 126 | for (size_t ii = 0; ii < Nex.size(); ii++) { 127 | Nex_std += (Nex[ii] - Nex_mean) * (Nex[ii] - Nex_mean); 128 | } 129 | Nex_std = std::sqrt(Nex_std / double(num_events)); 130 | 131 | for (size_t ii = 0; ii < Nion.size(); ii++) { 132 | Nion_std += (Nion[ii] - Nion_mean) * (Nion[ii] - Nion_mean); 133 | } 134 | Nion_std = std::sqrt(Nion_std / double(num_events)); 135 | 136 | output_file << particle_type[k] << ","; 137 | output_file << energy_vals[i] << ","; 138 | output_file << electric_field[v] << ","; 139 | output_file << (Nq_mean / energy_vals[i]) << ","; 140 | output_file << (Ne_mean / energy_vals[i]) << ","; 141 | output_file << (Nph_mean / energy_vals[i]) << ","; 142 | output_file << Nph_mean << ","; 143 | output_file << Ne_mean << ","; 144 | output_file << Nex_mean << ","; 145 | output_file << Nion_mean << ","; 146 | output_file << (Nq_std / energy_vals[i]) << ","; 147 | output_file << (Ne_std / energy_vals[i]) << ","; 148 | output_file << (Nph_std / energy_vals[i]) << ","; 149 | output_file << Nph_std << ","; 150 | output_file << Ne_std << ","; 151 | output_file << Nex_std << ","; 152 | output_file << Nion_std << "\n"; 153 | } 154 | } 155 | } 156 | output_file.close(); 157 | return 0; 158 | } -------------------------------------------------------------------------------- /G4integration/NESTProc.hh: -------------------------------------------------------------------------------- 1 | #ifndef NESTPROC_h 2 | #define NESTPROC_h 1 3 | 4 | #include 5 | 6 | #include "G4Track.hh" 7 | #include "G4DynamicParticle.hh" 8 | #include "G4Material.hh" 9 | #include "G4MaterialPropertiesTable.hh" 10 | #include "G4OpticalPhoton.hh" 11 | #include "G4ParticleMomentum.hh" 12 | #include "G4PhysicsOrderedFreeVector.hh" 13 | #include "G4Poisson.hh" 14 | #include "G4Step.hh" 15 | #include "G4ThreeVector.hh" 16 | #include "G4VRestDiscreteProcess.hh" 17 | #include "Randomize.hh" 18 | #include "globals.hh" 19 | #include "templates.hh" 20 | //#include "G4ThermalElectron.hh" 21 | #include "G4MaterialCutsCouple.hh" 22 | #include "G4ProductionCuts.hh" 23 | #include "G4SystemOfUnits.hh" 24 | #include "NEST.hh" 25 | 26 | namespace NEST { 27 | 28 | struct Hit { 29 | public: 30 | Hit(double _E, double _t, G4ThreeVector _xyz) 31 | : E(_E), t(_t), xyz(_xyz), result{0, 0, 0, 0, 0, 0} {}; 32 | double E; 33 | double t; 34 | G4ThreeVector xyz; 35 | QuantaResult result; 36 | }; 37 | 38 | struct Lineage { 39 | public: 40 | Lineage(INTERACTION_TYPE _type) : type(_type){}; 41 | INTERACTION_TYPE type = NoneType; 42 | std::vector hits; 43 | double density = -1; 44 | int A = -1; 45 | int Z = -1; 46 | NESTresult result; 47 | bool result_calculated = false; 48 | }; 49 | 50 | class NoTimeParticleChange : public G4ParticleChange { 51 | public: 52 | NoTimeParticleChange() : G4ParticleChange() { debugFlag = false; } 53 | }; 54 | 55 | class NESTThermalElectron : public G4ParticleDefinition { 56 | private: 57 | static NESTThermalElectron* theInstance; 58 | NESTThermalElectron() {} 59 | ~NESTThermalElectron() {} 60 | 61 | public: 62 | static NESTThermalElectron* Definition(); 63 | static NESTThermalElectron* ThermalElectronDefinition(); 64 | static NESTThermalElectron* ThermalElectron(); 65 | }; 66 | 67 | class NESTProc : public G4VRestDiscreteProcess { 68 | public: // constructor and destructor 69 | NESTProc(const G4String& processName, G4ProcessType type, 70 | VDetector* detector); 71 | NESTProc(const G4String& processName, G4ProcessType type, 72 | NESTcalc* customcalc, VDetector* detector); 73 | ~NESTProc(); 74 | 75 | public: // methods, with descriptions 76 | G4bool IsApplicable(const G4ParticleDefinition& aParticleType); 77 | // Returns true -> 'is applicable', for any particle type except for an 78 | // 'opticalphoton' and for short-lived particles 79 | 80 | G4double GetMeanFreePath(const G4Track& aTrack, G4double, G4ForceCondition*); 81 | // Returns infinity; i. e. the process does not limit the step, but 82 | // sets the 'StronglyForced' condition for the DoIt to be invoked at 83 | // every step. 84 | 85 | G4double GetMeanLifeTime(const G4Track& aTrack, G4ForceCondition*); 86 | // Returns infinity; i. e. the process does not limit the time, but 87 | // sets the 'StronglyForced' condition for the DoIt to be invoked at 88 | // every step. 89 | 90 | // For in-flight particles losing energy (or those stopped) 91 | G4VParticleChange* PostStepDoIt(const G4Track& aTrack, const G4Step& aStep); 92 | G4VParticleChange* AtRestDoIt(const G4Track& aTrack, const G4Step& aStep); 93 | 94 | void TryPopLineages(const G4Track& aTrack, const G4Step& aStep); 95 | 96 | void SetScintillationYieldFactor(const G4double yieldfactor); 97 | // Called to set the scintillation quantum yield factor, useful for 98 | // shutting off scintillation entirely, or for producing a universal 99 | // re-scaling to for example represent detector effects. Internally is 100 | // used for Lindhard yield factor for NR. Default should be user-set 101 | // to be 1 (for ER) in your simulation -- see NEST readme 102 | 103 | G4double GetScintillationYieldFactor() const; 104 | // Returns the quantum (photon/electron) yield factor. See above. 105 | 106 | Lineage GetChildType(const G4Track* aTrack, const G4Track* sec) const; 107 | G4Track* MakePhoton(G4ThreeVector xyz, double t); 108 | G4Track* MakeElectron(G4ThreeVector xyz, double density, double t, 109 | double kin_E); 110 | std::vector getLastLineages() const { 111 | return lineages_prevEvent; 112 | } 113 | void SetDetailedSecondaries(bool detailed) { 114 | detailed_secondaries = detailed; 115 | } 116 | void SetStackElectrons(bool stack_e) { stack_electrons = stack_e; } 117 | void SetStackPhotons(bool stack_ph) { stack_photons = stack_ph; } 118 | void SetAnalysisTrigger( 119 | std::function)> _analysisTrigger) { 120 | this->analysisTrigger = _analysisTrigger; 121 | } 122 | 123 | void SetGamma_break(double _gamma_break) { this->gamma_break = _gamma_break; } 124 | 125 | double GetGamma_break() const { return gamma_break; } 126 | 127 | void SetNESTcalc(std::unique_ptr newcalc) { 128 | fNESTcalc.reset(newcalc.release()); 129 | fDetector.reset(newcalc->GetDetector()); 130 | } 131 | 132 | protected: 133 | // bools for tracking some special particle cases 134 | 135 | std::unique_ptr fNESTcalc = NULL; 136 | std::vector lineages; 137 | std::vector lineages_prevEvent; 138 | std::map, uint64_t> 139 | track_lins; 140 | std::unique_ptr fDetector; 141 | NoTimeParticleChange fParticleChange; 142 | 143 | G4double YieldFactor = 1; // turns scint. on/off 144 | bool detailed_secondaries = true; 145 | bool stack_electrons = true; 146 | bool stack_photons = true; 147 | double gamma_break = 148 | 9 * mm; // Gammas will not pass on their lineage (if they have one, e.g. 149 | // bremsstrahlung) if they are this far from their origin. 150 | int verbose = 0; 151 | 152 | std::function)> analysisTrigger; 153 | }; 154 | 155 | //////////////////// 156 | // Inline methods 157 | //////////////////// 158 | 159 | inline G4bool NESTProc::IsApplicable( 160 | const G4ParticleDefinition& aParticleType) { 161 | if (aParticleType.GetParticleName() == "opticalphoton") return false; 162 | if (aParticleType.IsShortLived()) return false; 163 | if (aParticleType.GetParticleName() == "thermalelectron") return false; 164 | // if(abs(aParticleType.GetPDGEncoding())==2112 || //neutron (no E-dep.) 165 | return true; 166 | } 167 | 168 | inline void NESTProc::SetScintillationYieldFactor(const G4double yieldfactor) { 169 | YieldFactor = yieldfactor; 170 | } 171 | 172 | inline G4double NESTProc::GetScintillationYieldFactor() const { 173 | return YieldFactor; 174 | } 175 | 176 | } // namespace NEST 177 | #endif /* NESTPROC_h */ 178 | -------------------------------------------------------------------------------- /utils/larnest/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Various utilities for plotting, etc. 3 | """ 4 | import numpy as np 5 | from matplotlib import pyplot as plt 6 | import csv 7 | 8 | 9 | def generate_plot_grid( 10 | num_plots, 11 | **kwargs, 12 | ): 13 | """ 14 | Generate a grid of N plots, excluding extra 15 | ones which are not used. 16 | """ 17 | nrows = int(np.floor(np.sqrt(num_plots))) 18 | ncols = int(np.ceil(num_plots/nrows)) 19 | fig, axs = plt.subplots( 20 | nrows=nrows, ncols=ncols, 21 | **kwargs 22 | ) 23 | nplots = nrows * ncols 24 | nextra = nplots - num_plots 25 | for ii in range(nextra): 26 | axs.flat[-(ii+1)].set_visible(False) 27 | return fig, axs 28 | 29 | def compile_nr_total_data( 30 | data_dir: str="data/", 31 | ): 32 | nr_total = { 33 | "Dataset": [], 34 | "energy": [], 35 | "energy_sl":[], 36 | "energy_sh":[], 37 | "field": [], 38 | "field_sl": [], 39 | "field_sh": [], 40 | "yield": [], 41 | "yield_sl": [], 42 | "yield_sh": [] 43 | } 44 | with open(f"{data_dir}nr_total_alt.csv","r") as file: 45 | reader = csv.reader(file, delimiter=",") 46 | next(reader) 47 | for row in reader: 48 | nr_total["Dataset"].append(row[0]) 49 | nr_total["energy"].append(float(row[1])) 50 | nr_total["energy_sl"].append(float(row[2])) 51 | nr_total["energy_sh"].append(float(row[3])) 52 | nr_total["field"].append(float(row[4])) 53 | nr_total["field_sl"].append(float(row[5])) 54 | nr_total["field_sh"].append(float(row[6])) 55 | nr_total["yield"].append(float(row[7])) 56 | nr_total["yield_sl"].append(float(row[8])) 57 | nr_total["yield_sh"].append(float(row[9])) 58 | return {key: np.array(nr_total[key]) for key in nr_total} 59 | 60 | def compile_nr_charge_data( 61 | data_dir: str="data/", 62 | ): 63 | nr_charge = { 64 | "Dataset": [], 65 | "energy": [], 66 | "energy_sl":[], 67 | "energy_sh":[], 68 | "field": [], 69 | "field_sl": [], 70 | "field_sh": [], 71 | "yield": [], 72 | "yield_sl": [], 73 | "yield_sh": [] 74 | } 75 | with open(f"{data_dir}nr_charge_alt.csv","r") as file: 76 | reader = csv.reader(file, delimiter=",") 77 | next(reader) 78 | for row in reader: 79 | nr_charge["Dataset"].append(row[0]) 80 | nr_charge["energy"].append(float(row[1])) 81 | nr_charge["energy_sl"].append(float(row[2])) 82 | nr_charge["energy_sh"].append(float(row[3])) 83 | nr_charge["field"].append(float(row[4])) 84 | nr_charge["field_sl"].append(float(row[5])) 85 | nr_charge["field_sh"].append(float(row[6])) 86 | nr_charge["yield"].append(float(row[7])) 87 | nr_charge["yield_sl"].append(float(row[8])) 88 | nr_charge["yield_sh"].append(float(row[9])) 89 | return {key: np.array(nr_charge[key]) for key in nr_charge} 90 | 91 | def compile_nr_light_data( 92 | data_dir: str="data/", 93 | ): 94 | nr_light = { 95 | "Dataset": [], 96 | "energy": [], 97 | "energy_sl":[], 98 | "energy_sh":[], 99 | "field": [], 100 | "field_sl": [], 101 | "field_sh": [], 102 | "yield": [], 103 | "yield_sl": [], 104 | "yield_sh": [] 105 | } 106 | with open(f"{data_dir}nr_light_alt.csv","r") as file: 107 | reader = csv.reader(file, delimiter=",") 108 | next(reader) 109 | for row in reader: 110 | nr_light["Dataset"].append(row[0]) 111 | nr_light["energy"].append(float(row[1])) 112 | nr_light["energy_sl"].append(float(row[2])) 113 | nr_light["energy_sh"].append(float(row[3])) 114 | nr_light["field"].append(float(row[4])) 115 | nr_light["field_sl"].append(float(row[5])) 116 | nr_light["field_sh"].append(float(row[6])) 117 | nr_light["yield"].append(float(row[7])) 118 | nr_light["yield_sl"].append(float(row[8])) 119 | nr_light["yield_sh"].append(float(row[9])) 120 | return {key: np.array(nr_light[key]) for key in nr_light} 121 | 122 | def compile_er_charge_data( 123 | data_dir: str="data/", 124 | ): 125 | er_charge = { 126 | "Dataset": [], 127 | "energy": [], 128 | "energy_sl":[], 129 | "energy_sh":[], 130 | "field": [], 131 | "field_sl": [], 132 | "field_sh": [], 133 | "yield": [], 134 | "yield_sl": [], 135 | "yield_sh": [] 136 | } 137 | with open(f"{data_dir}er_charge.csv","r") as file: 138 | reader = csv.reader(file, delimiter=",") 139 | next(reader) 140 | for row in reader: 141 | er_charge["Dataset"].append(row[0]) 142 | er_charge["energy"].append(float(row[1])) 143 | er_charge["energy_sl"].append(float(row[2])) 144 | er_charge["energy_sh"].append(float(row[3])) 145 | er_charge["field"].append(float(row[4])) 146 | er_charge["field_sl"].append(float(row[5])) 147 | er_charge["field_sh"].append(float(row[6])) 148 | er_charge["yield"].append(float(row[7])) 149 | er_charge["yield_sl"].append(float(row[8])) 150 | er_charge["yield_sh"].append(float(row[9])) 151 | return {key: np.array(er_charge[key]) for key in er_charge} 152 | 153 | def compile_er_light_data( 154 | data_dir: str="data/", 155 | ): 156 | er_light = { 157 | "Dataset": [], 158 | "energy": [], 159 | "energy_sl":[], 160 | "energy_sh":[], 161 | "field": [], 162 | "field_sl": [], 163 | "field_sh": [], 164 | "yield": [], 165 | "yield_sl": [], 166 | "yield_sh": [] 167 | } 168 | with open(f"{data_dir}er_light.csv","r") as file: 169 | reader = csv.reader(file, delimiter=",") 170 | next(reader) 171 | for row in reader: 172 | er_light["Dataset"].append(row[0]) 173 | er_light["energy"].append(float(row[1])) 174 | er_light["energy_sl"].append(float(row[2])) 175 | er_light["energy_sh"].append(float(row[3])) 176 | er_light["field"].append(float(row[4])) 177 | er_light["field_sl"].append(float(row[5])) 178 | er_light["field_sh"].append(float(row[6])) 179 | er_light["yield"].append(float(row[7])) 180 | er_light["yield_sl"].append(float(row[8])) 181 | er_light["yield_sh"].append(float(row[9])) 182 | return {key: np.array(er_light[key]) for key in er_light} 183 | 184 | def compile_lar_data( 185 | data_dir: str="data/", 186 | ): 187 | np.savez( 188 | f"{data_dir}lar_data.npz", 189 | nr_total=compile_nr_total_data(data_dir), 190 | nr_charge=compile_nr_charge_data(data_dir), 191 | nr_light=compile_nr_light_data(data_dir), 192 | er_charge=compile_er_charge_data(data_dir), 193 | er_light=compile_er_light_data(data_dir), 194 | ) 195 | 196 | if __name__ == "__main__": 197 | 198 | compile_lar_data() -------------------------------------------------------------------------------- /include/Detectors/Detector_G3.hh: -------------------------------------------------------------------------------- 1 | // 2 | // DetectorExample_XENON10.hh 3 | // 4 | // Added by Quentin Riffard, September 10, 2021 5 | // 6 | // This header file is a preliminary attempt to add a G3 detector 7 | // to be used for sensitivity and projection studies. 8 | // 9 | // Note: Parameters here are subject to change based on future 10 | // performance studies 11 | // 12 | #ifndef NEST_DETECTOR_G3_HH 13 | #define NEST_DETECTOR_G3_HH 14 | 15 | #include "VDetector.hh" 16 | 17 | using namespace std; 18 | 19 | class Detector_G3 : public VDetector { 20 | public: 21 | Detector_G3() { 22 | //cerr << "You are currently using the projected G3 detector." << endl 23 | // << endl; 24 | 25 | // Call the initialisation of all the parameters 26 | Initialization(); 27 | } 28 | 29 | ~Detector_G3() override = default; 30 | 31 | void Initialization() override { 32 | name = "projected G3"; 33 | // Primary Scintillation (S1) parameters 34 | g1 = 0.118735; // phd per S1 phot at dtCntr (not phe). Divide out 2-PE 35 | // effect 36 | sPEres = 0.38; // single phe resolution (Gaussian assumed) 37 | sPEthr = 0.375; // POD threshold in phe, usually used IN PLACE of sPEeff 38 | sPEeff = 1.0; // actual efficiency, can be used in lieu of POD threshold 39 | noiseBaseline[0] = 0.0; // baseline noise mean and width in PE (Gaussian) 40 | noiseBaseline[1] = 0.0; // baseline noise mean and width in PE (Gaussian) 41 | noiseBaseline[2] = 0.0; 42 | noiseBaseline[3] = 0.0; 43 | P_dphe = 0.2; // chance 1 photon makes 2 phe instead of 1 in Hamamatsu PMT 44 | 45 | coinWind = 150; // S1 coincidence window in ns 46 | coinLevel = 3; // how many PMTs have to fire for an S1 to count 47 | numPMTs = 494; // For coincidence calculation 48 | 49 | // Ionization and Secondary Scintillation (S2) parameters 50 | g1_gas = 0.1018; // phd per S2 photon in gas, used to get SE size 51 | s2Fano = 2.0; // Fano-like fudge factor for SE width 52 | s2_thr = 53 | 470. * 54 | (1.0 + 55 | P_dphe); // the S2 threshold in phe or PE, *not* phd. Affects NR most 56 | E_gas = 10.821; // field in kV/cm between liquid/gas border and anode 57 | eLife_us = 850.; // the drift electron mean lifetime in micro-seconds 58 | 59 | // Thermodynamic Properties 60 | inGas = false; 61 | T_Kelvin = 177.; // for liquid drift speed calculation 62 | p_bar = 2.14; // gas pressure in units of bars, it controls S2 size 63 | // if you are getting warnings about being in gas, lower T and/or raise p 64 | 65 | // Data Analysis Parameters and Geometry 66 | dtCntr = 505.123; // center of detector for S1 corrections, in usec. 67 | dt_min = 0.01; // minimum. Top of detector fiducial volume 68 | dt_max = 894.74; // maximum. Bottom of detector fiducial volume 69 | 70 | radius = 728.; // millimeters (fiducial rad) 71 | radmax = 728.; // actual physical geo. limit 72 | 73 | TopDrift = 1461.; // mm not cm or us (but, this *is* where dt=0) 74 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 75 | // PMTs) 76 | // In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, not 77 | // anode! 78 | anode = 1469. - 0.2568; // the level of the anode grid-wire plane in mm 79 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 80 | // above it 81 | gate = 1456.; // mm. This is where the E-field changes (higher) 82 | // in gas detectors, the gate is still the gate, but it's where S2 starts 83 | cathode = 0.; // mm. Defines point below which events are gamma-X 84 | 85 | // 2-D (X & Y) Position Reconstruction 86 | PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 87 | PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 88 | 89 | OldW13eV = true; 90 | 91 | noiseLinear[0] = 0; 92 | noiseLinear[1] = 0; 93 | } 94 | 95 | // S1 PDE custom fit for function of z 96 | // s1polA + s1polB*z[mm] + s1polC*z^2+... (QE included, for binom dist) e.g. 97 | double FitS1(double xPos_mm, double yPos_mm, double zPos_mm, 98 | LCE map) override { 99 | return 1.; // No S1 corrections 100 | } 101 | 102 | // Drift electric field as function of Z in mm 103 | // For example, use a high-order poly spline 104 | double FitEF(double xPos_mm, double yPos_mm, 105 | double zPos_mm) override { // in V/cm 106 | return 180.; // Simple uniform field 107 | } 108 | 109 | // S2 PDE custom fit for function of r 110 | // s2polA + s2polB*r[mm] + s2polC*r^2+... (QE included, for binom dist) e.g. 111 | double FitS2(double xPos_mm, double yPos_mm, LCE map) override { 112 | return 1.; // No S2 corrections 113 | } 114 | 115 | vector FitTBA(double xPos_mm, double yPos_mm, 116 | double zPos_mm) override { 117 | vector BotTotRat(2); 118 | 119 | BotTotRat[0] = 0.6; // S1 bottom-to-total ratio 120 | BotTotRat[1] = 0.4; // S2 bottom-to-total ratio, typically only used for 121 | // position recon (1-this) 122 | 123 | return BotTotRat; 124 | } 125 | 126 | double OptTrans(double xPos_mm, double yPos_mm, double zPos_mm) override { 127 | double phoTravT, approxCenter = (TopDrift + cathode) / 2., 128 | relativeZ = zPos_mm - approxCenter; 129 | 130 | double A = 0.048467 - 7.6386e-6 * relativeZ + 131 | 1.2016e-6 * pow(relativeZ, 2.) - 6.0833e-9 * pow(relativeZ, 3.); 132 | if (A < 0.) A = 0.; // cannot have negative probability 133 | double B_a = 0.99373 + 0.0010309 * relativeZ - 134 | 2.5788e-6 * pow(relativeZ, 2.) - 135 | 1.2000e-8 * pow(relativeZ, 3.); 136 | double B_b = 1. - B_a; 137 | double tau_a = 11.15; // all times in nanoseconds 138 | double tau_b = 4.5093 + 0.03437 * relativeZ - 139 | 0.00018406 * pow(relativeZ, 2.) - 140 | 1.6383e-6 * pow(relativeZ, 3.); 141 | if (tau_b < 0.) tau_b = 0.; // cannot have negative time 142 | 143 | // A = 0.0574; B_a = 1.062; tau_a = 11.1; tau_b = 2.70; B_b = 1.0 - B_a; 144 | // //LUX D-D conditions 145 | 146 | if (RandomGen::rndm()->rand_uniform() < A) 147 | phoTravT = 0.; // direct travel time to PMTs (low) 148 | else { // using P0(t) = 149 | // A*delta(t)+(1-A)*[(B_a/tau_a)e^(-t/tau_a)+(B_b/tau_b)e^(-t/tau_b)] 150 | // LUX PSD paper, but should apply to all detectors w/ diff #'s 151 | if (RandomGen::rndm()->rand_uniform() < B_a) 152 | phoTravT = -tau_a * log(RandomGen::rndm()->rand_uniform()); 153 | else 154 | phoTravT = -tau_b * log(RandomGen::rndm()->rand_uniform()); 155 | } 156 | 157 | double sig = RandomGen::rndm()->rand_gauss( 158 | 3.84, .09, true); // includes stat unc but not syst 159 | phoTravT += RandomGen::rndm()->rand_gauss( 160 | 0.00, sig, 161 | false); // the overall width added to photon time spectra by the 162 | // effects in the electronics and the data reduction 163 | // pipeline 164 | 165 | if (phoTravT > DBL_MAX) phoTravT = tau_a; 166 | if (phoTravT < -DBL_MAX) phoTravT = 0.000; 167 | 168 | return phoTravT; // this function follows LUX (arXiv:1802.06162) not Xe10 169 | // technically but tried to make general 170 | } 171 | 172 | vector SinglePEWaveForm(double area, double t0) override { 173 | vector PEperBin; 174 | 175 | double threshold = PULSEHEIGHT; // photo-electrons 176 | double sigma = PULSE_WIDTH; // ns 177 | area *= 10. * (1. + threshold); 178 | double amplitude = area / (sigma * sqrt(2. * M_PI)), 179 | signal; // assumes perfect Gaussian 180 | 181 | double tStep1 = SAMPLE_SIZE / 1e2; // ns, make sure much smaller than 182 | // sample size; used to generate MC-true 183 | // pulses essentially 184 | double tStep2 = 185 | SAMPLE_SIZE; // ns; 1 over digitization rate, 100 MHz assumed here 186 | 187 | double time = -5. * sigma; 188 | bool digitizeMe = false; 189 | while (true) { 190 | signal = amplitude * exp(-pow(time, 2.) / (2. * sigma * sigma)); 191 | if (signal < threshold) { 192 | if (digitizeMe) 193 | break; 194 | else 195 | ; // do nothing - goes down to advancing time block 196 | } else { 197 | if (digitizeMe) 198 | PEperBin.push_back(signal); 199 | else { 200 | if (RandomGen::rndm()->rand_uniform() < 2. * (tStep1 / tStep2)) { 201 | PEperBin.push_back(time + t0); 202 | PEperBin.push_back(signal); 203 | digitizeMe = true; 204 | } else { 205 | } 206 | } 207 | } 208 | if (digitizeMe) 209 | time += tStep2; 210 | else 211 | time += tStep1; 212 | if (time > 5. * sigma) break; 213 | } 214 | return PEperBin; 215 | } 216 | }; 217 | 218 | #endif // NEST_DETECTOR_G3_HH 219 | -------------------------------------------------------------------------------- /examples/pulseShape.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "analysis_G2.hh" 8 | 9 | unsigned long int NUMEVT; // first global var: #lines from file 10 | #define S2BORD 1000 // ns - border for S1/S2 discrimination 11 | #define RISEDEF \ 12 | 0.05 // 5% rise time used as the t0 point. 10% for seminal Kwong paper 13 | #define UNC 0.35 // uncertainty in definition of rise time 14 | // 35% for Run04, but all-natural 0% better for Run03 and Xed 15 | #define LOSTAREA \ 16 | 1.015 // O(1%) adjustment for untraceable tiny bug in S2 areas. But not 17 | // universal? 18 | 19 | using namespace std; 20 | 21 | int main ( int argc, char** argv ) { 22 | 23 | double S1LimPE[2] = { minS1, maxS1 }; 24 | 25 | if ( verbosity < 2 ) { 26 | if ( argc < 3 ) { 27 | cerr << "2 args needed: S1 (non-c) in phe (NOT phd). 1st value is min, 2nd " 28 | "max." 29 | << endl; 30 | return EXIT_FAILURE; 31 | } 32 | else { 33 | S1LimPE[0] = atof(argv[1]); S1LimPE[1] = atof(argv[2]); // avoid Seg Fault! 34 | } 35 | } 36 | 37 | double MaxPossibleTime = DBL_MAX; default_random_engine generator; 38 | uniform_real_distribution distribution( 39 | 0., 20.); // ns for ran offset. 2 bin default. For Case: -7,0 40 | // for LUX Run03: better results with -4 to 16. Still 2 samples-wide, but 41 | // different centering 42 | vector num, win; 43 | char line[60]; 44 | vector tns, bot, top; 45 | FILE* ifp = fopen("photon_times.txt", "r"); 46 | double a, b, c, d, e; 47 | uint64_t i = 0; 48 | 49 | while (1) { 50 | if (i == 0) { 51 | while (getc(ifp) != '\n') { 52 | ; 53 | } 54 | i = 1; 55 | continue; 56 | } 57 | if (fgets(line, 40, ifp) == NULL) { 58 | NUMEVT = unsigned(a) + 1; 59 | if ( NUMEVT % 10 ) 60 | cerr << "Warning! Check to see if last event(s) missing b/c S1=0.\n"; 61 | break; 62 | } else ; 63 | if ( verbosity < 2 ) 64 | sscanf(line, "%lf\t%lf\t%lf\t%lf\t%lf", &a, &b, &c, &d, &e); 65 | else 66 | sscanf(line, "%lf\t%lf", &a, &b); 67 | if (feof(ifp)) break; 68 | num.push_back(uint64_t(a)); 69 | tns.push_back(b); 70 | if ( verbosity < 2 ) { 71 | if (b > S2BORD) e = 0; 72 | bot.push_back(c); 73 | top.push_back(d); 74 | win.push_back(uint64_t(e)); 75 | } 76 | // cerr << a << " " << b << " " << c << " " << d << " " << e << endl; 77 | } 78 | fclose(ifp); 79 | 80 | if ( verbosity >= 2 ) { //for pseudo-S1-only spike mode 81 | double range[4] = {-200., 200., -69.7, 196.2}; 82 | vector means(NUMEVT, 0.); 83 | vector count(NUMEVT, 0.); 84 | vector S1total(NUMEVT, 0.); 85 | vector S1inner(NUMEVT, 0.); 86 | for ( i = 0; i < num.size(); ++i ) { 87 | if ( tns[i] < 1e4 ) { 88 | ++count[num[i]]; 89 | means[num[i]] += tns[i]; 90 | } 91 | } 92 | for ( i = 0; i < NUMEVT; ++i ) means[i] /= count[i]; 93 | for ( i = 0; i < num.size(); ++i ) { 94 | tns[i] -= means[num[i]]; //correction on means 95 | if ( tns[i] >= range[0] && tns[i] <= range[1] ) 96 | ++S1total[num[i]]; 97 | if ( tns[i] >= range[2] && tns[i] <= range[3] ) 98 | ++S1inner[num[i]]; 99 | } 100 | cout << "evt#\tpromptFrac" << endl; 101 | for ( i = 0; i < NUMEVT; ++i ) { 102 | double promptFrac = 0.0; 103 | if ( S1total[i] > 0. ) 104 | promptFrac = S1inner[i] / S1total[i]; 105 | cout << i << "\t" << promptFrac << endl; 106 | } 107 | return EXIT_SUCCESS; 108 | } 109 | 110 | vector S1tot(NUMEVT, 0.); 111 | vector S2tot(NUMEVT, 0.); 112 | vector S2max(NUMEVT, 0.); 113 | vector CI_left(NUMEVT, 0.); 114 | vector CI_right(NUMEVT, 0.); 115 | vector S2width(NUMEVT, 0.); 116 | vector S1f30(NUMEVT, 0.); 117 | vector T0X(NUMEVT, 0.); 118 | vector driftT(NUMEVT, 0.); 119 | 120 | for (i = 0; i < num.size(); ++i) { 121 | T0X[num[i]] = MaxPossibleTime; 122 | if (tns[i] < S2BORD) 123 | S1tot[num[i]] += top[i] + bot[i]; 124 | else { 125 | S2tot[num[i]] += (top[i] + bot[i]) * LOSTAREA; 126 | if ((top[i] + bot[i]) > S2max[num[i]]) { 127 | S2max[num[i]] = top[i] + bot[i]; 128 | driftT[num[i]] = 129 | tns[i] - 130 | S2BORD; // minus 1 microsecond fudge factor to get mean drift 131 | } else 132 | ; 133 | } 134 | } 135 | double fraction = 0.0225, 136 | soFar = 0.; // default Confidence Interval is 95.5% (~2-sigma) 137 | for (i = 0; i < num.size(); ++i) { 138 | if (tns[i] >= S2BORD && S2width[num[i]] == 0.) { 139 | soFar += (top[i] + bot[i]) * LOSTAREA; 140 | if (soFar > fraction * S2tot[num[i]] && CI_left[num[i]] == 0. && 141 | CI_right[num[i]] == 0.) 142 | CI_left[num[i]] = tns[i]; 143 | if (soFar > 0.500000 * S2tot[num[i]] && CI_right[num[i]] == 0. && 144 | CI_left[num[i]] != 0.) 145 | CI_right[num[i]] = tns[i]; // left half only because of asymmetry 146 | if (CI_left[num[i]] != 0. && CI_right[num[i]] != 0.) { 147 | S2width[num[i]] = (CI_right[num[i]] - CI_left[num[i]]) / 2.; 148 | soFar = 0.0; 149 | continue; 150 | } // div by 2 as 1s--1s=2sigma 151 | } 152 | } 153 | soFar = 0.00; 154 | normal_distribution distribution2(RISEDEF, RISEDEF * UNC); 155 | for (i = 0; i < num.size(); ++i) { 156 | if (tns[i] < S2BORD && T0X[num[i]] >= MaxPossibleTime) { 157 | soFar += top[i] + bot[i]; 158 | fraction = distribution2(generator); 159 | if (fraction < 0.) fraction = 0.; 160 | if (soFar > (fraction * S1tot[num[i]]) && 161 | T0X[num[i]] >= MaxPossibleTime) { 162 | T0X[num[i]] = tns[i]; //time of "actual" S1 peak start 163 | soFar = 0.0; 164 | continue; 165 | } 166 | } 167 | } 168 | 169 | double area[2] = {0.0, 0.0}; 170 | double range[4] = {-14., 134., -8., 32.}; 171 | double rRange[4] = {range[0], range[1], range[2], 172 | range[3]}; // Dahl: -50,300,-50,10 173 | for (i = 0; i < num.size(); ++i) { 174 | if (T0X[num[i]] < MaxPossibleTime) tns[i] -= T0X[num[i]]; 175 | if (tns[i] < (S2BORD - T0X[num[i]]) && win[i] == 1) { 176 | if (tns[i] > rRange[0] && tns[i] < rRange[1]) { 177 | area[0] += top[i] + bot[i]; 178 | if (tns[i] > rRange[2] && tns[i] < rRange[3]) 179 | area[1] += top[i] + bot[i]; 180 | } 181 | } else { 182 | double offset0 = 183 | 0.0 + 184 | distribution(generator); // consider unique offset for each 185 | // range[]. In original try makes bumps 186 | // double offset1 = 10.+distribution(generator); 187 | // double offset2 = 10.+distribution(generator); 188 | // double offset3 = 10.+distribution(generator); 189 | // randomized offset was to create extra smearing to better match real data with unknowns in the DAQ 190 | rRange[0] = range[0] + offset0; 191 | rRange[1] = range[1] + offset0; 192 | rRange[2] = range[2] + offset0; 193 | rRange[3] = range[3] + offset0; 194 | if (S1f30[num[i]] == 0. && area[0] != 0. && area[1] != 0. && 195 | area[0] != area[1]) 196 | S1f30[num[i]] = area[1] / area[0]; 197 | area[0] = 0.0; 198 | area[1] = 0.0; 199 | } 200 | } 201 | 202 | double mean = 0.0; 203 | int j = 0; 204 | vector NonBlank; 205 | cout << "evt#\tS1, pe\tpromptFrac\tdrift [ms]\tS2, pe\tWidth [ns]\n"; 206 | for (i = 0; i < NUMEVT; ++i) { 207 | // cerr << T0X[i] << endl; 208 | if (S1tot[i] > S1LimPE[0] && S2tot[i] > 0. && S1f30[i] > 0. && 209 | S1f30[i] < 1. && S1tot[i] < S1LimPE[1]) { 210 | mean += S1f30[i]; 211 | ++j; 212 | cout << i << "\t" << S1tot[i] << "\t" << S1f30[i] << "\t" 213 | << driftT[i] / 1e6 << "\t" << S2tot[i] << "\t" << S2width[i] << endl; 214 | NonBlank.push_back(S1f30[i]); 215 | } else 216 | cout << i << endl; 217 | } 218 | if (j == 0) { 219 | cerr << "no events in range!\n"; 220 | return EXIT_FAILURE; 221 | } 222 | mean /= double(j); 223 | std::sort(NonBlank.begin(), NonBlank.end()); 224 | double median = NonBlank[j / 2]; 225 | 226 | double sigma = 0.0; 227 | for (i = 0; i < NUMEVT; ++i) 228 | if (S1f30[i] != 0. && S1tot[i] > S1LimPE[0] && S1tot[i] < S1LimPE[1]) 229 | sigma += (mean - S1f30[i]) * (mean - S1f30[i]); 230 | sigma /= (double(j) - 1.); 231 | sigma = sqrt(sigma); 232 | double skew = 0.0; 233 | for (i = 0; i < NUMEVT; ++i) 234 | if (S1f30[i] != 0. && S1tot[i] > S1LimPE[0] && S1tot[i] < S1LimPE[1]) 235 | skew += pow((S1f30[i] - mean) / sigma, 3.); 236 | skew /= double(j); 237 | cerr << "MEDIAN\t\tSIGMA\t\tSKEWNESS\n"; 238 | cerr << median << "\t" << sigma << "\t" << skew 239 | << endl; // can switch to "mean," as desired, but change the header name 240 | // too :) 241 | 242 | return EXIT_SUCCESS; 243 | 244 | } 245 | -------------------------------------------------------------------------------- /examples/GitDiffDetPerf.dat: -------------------------------------------------------------------------------- 1 | diff --git a/include/Detectors/LUX_Run03.hh b/include/Detectors/LUX_Run03.hh 2 | index a3968f6..caccb9b 100644 3 | --- a/include/Detectors/LUX_Run03.hh 4 | +++ b/include/Detectors/LUX_Run03.hh 5 | @@ -26,7 +26,7 @@ class DetectorExample_LUX_RUN03 : public VDetector { 6 | // function of time 7 | void Initialization() override { 8 | // Primary Scintillation (S1) parameters 9 | - g1 = 0.1170; // 0.117+/-0.003 WS,0.115+/-0.005 D-D,0.115+/-0.005 10 | + g1 = 0.09; // 0.117+/-0.003 WS,0.115+/-0.005 D-D,0.115+/-0.005 11 | // CH3T,0.119+/-0.001 LUXSim. UNITS: phd per photon (NOT 12 | // photoelectrons!!) 13 | sPEres = 0.37; // arXiv:1910.04211. UNITS: phe a.k.a. PE or photoelectrons 14 | @@ -45,19 +45,19 @@ class DetectorExample_LUX_RUN03 : public VDetector { 15 | 16 | OldW13eV = true; // default true, which means use "classic" W instead of 17 | // Baudis / EXO's 18 | - noiseLinear[0] = 0.0e-2; // 1910.04211 p.12, to match 1610.02076 Fig. 8. 19 | + noiseLinear[0] = 10.e-2; // 1910.04211 p.12, to match 1610.02076 Fig. 8. 20 | // UNITS: fraction NOT %! 21 | - noiseLinear[1] = 9.0e-2; // 1910.04211 p.12, to match 1610.02076 Fig. 8. 22 | + noiseLinear[1] = 5.0e-2; // 1910.04211 p.12, to match 1610.02076 Fig. 8. 23 | // UNITS: fraction NOT %! 24 | 25 | // Ionization and Secondary Scintillation (S2) parameters 26 | g1_gas = 0.1; // 0.1 in 1910.04211. UNITS: phd per e- 27 | s2Fano = 3.6; // 3.7 in 1910.04211; this matches 1608.05381 better. 28 | // Dimensionless 29 | - s2_thr = (150. * 1.173) / 0.915; // 65-194 pe in 1608.05381. UNITS: phe 30 | - E_gas = 6.25; // 6.55 in 1910.04211. UNITS: kV/cm 31 | + s2_thr = 200.; // 65-194 pe in 1608.05381. UNITS: phe 32 | + E_gas = 8.269; // 6.55 in 1910.04211. UNITS: kV/cm 33 | eLife_us = 34 | - 800.; // p.44 of James Verbus PhD thesis Brown. UNIT: microseconds (us) 35 | + 1e3; // p.44 of James Verbus PhD thesis Brown. UNIT: microseconds (us) 36 | 37 | // Thermodynamic Properties 38 | // inGas = false; //duh 39 | @@ -66,9 +66,9 @@ class DetectorExample_LUX_RUN03 : public VDetector { 40 | 41 | // Data Analysis Parameters and Geometry 42 | dtCntr = 43 | - 160.; // p.61 Dobi thesis UMD, 159 in 1708.02566. UNITS: microseconds 44 | - dt_min = 38.; // 1608.05381. UNITS: microseconds 45 | - dt_max = 305.; // 1608.05381. UNITS: microseconds 46 | + 150.; // p.61 Dobi thesis UMD, 159 in 1708.02566. UNITS: microseconds 47 | + dt_min = 0.00; // 1608.05381. UNITS: microseconds 48 | + dt_max = 300.; // 1608.05381. UNITS: microseconds 49 | 50 | radius = 200.; // 1512.03506. UNITS: mm 51 | radmax = 235.; // 1910.04211. UNITS: mm 52 | @@ -76,7 +76,7 @@ class DetectorExample_LUX_RUN03 : public VDetector { 53 | TopDrift = 544.95; // 544.95 in 1910.04211. UNITS: mm 54 | anode = 549.2; // 1910.04211 and 549 in 1708.02566. UNITS: mm 55 | gate = 539.2; // 1910.04211 and 539 in 1708.02566. UNITS: mm 56 | - cathode = 55.90; // 55.9-56 in 1910.04211,1708.02566. UNITS: mm 57 | + cathode = 0.;//55.90; // 55.9-56 in 1910.04211,1708.02566. UNITS: mm 58 | 59 | // 2-D (X & Y) Position Reconstruction 60 | PosResExp = 0.015; // arXiv:1710.02752 indirectly. UNITS: mm^-1 61 | @@ -94,12 +94,12 @@ class DetectorExample_LUX_RUN03 : public VDetector { 62 | finalCorr /= 307.9; 63 | if ((finalCorr < 0.5 || finalCorr > 1.5 || std::isnan(finalCorr)) && 64 | radius < radmax) { 65 | - cerr << "ERR: S1 corrections exceed a 50% difference. Are you sure you " 66 | - "didn't forget to change LUX numbers for your own detector??" 67 | - << endl; 68 | + //cerr << "ERR: S1 corrections exceed a 50% difference. Are you sure you " 69 | + // "didn't forget to change LUX numbers for your own detector??" 70 | + // << endl; 71 | return 1.; 72 | } else 73 | - return finalCorr; 74 | + return 1.;//finalCorr; 75 | } 76 | 77 | // Drift electric field as function of Z in mm 78 | @@ -135,12 +135,12 @@ class DetectorExample_LUX_RUN03 : public VDetector { 79 | finalCorr /= 9156.3; 80 | if ((finalCorr < 0.5 || finalCorr > 1.5 || std::isnan(finalCorr)) && 81 | radius < radmax) { 82 | - cerr << "ERR: S2 corrections exceed a 50% difference. Are you sure you " 83 | - "didn't forget to change LUX numbers for your own detector??" 84 | - << endl; 85 | + //cerr << "ERR: S2 corrections exceed a 50% difference. Are you sure you " 86 | + // "didn't forget to change LUX numbers for your own detector??" 87 | + // << endl; 88 | return 1.; 89 | } else 90 | - return finalCorr; 91 | + return 1.;//finalCorr; 92 | } 93 | 94 | vector FitTBA(double xPos_mm, double yPos_mm, 95 | diff --git a/include/NEST/analysis.hh b/include/NEST/analysis.hh 96 | index 6cee251..e4908d5 100644 97 | --- a/include/NEST/analysis.hh 98 | +++ b/include/NEST/analysis.hh 99 | @@ -14,7 +14,7 @@ bool MCtruthE = false; // false means reconstructed energy 100 | bool MCtruthPos = false; // false means reconstructed position 101 | 102 | // Setting the S1 and S2 calculation modes 103 | -NEST::S1CalculationMode s1CalculationMode = NEST::S1CalculationMode::Hybrid; 104 | +NEST::S1CalculationMode s1CalculationMode = NEST::S1CalculationMode::Full; 105 | // S1 calculation mode options are: 106 | // Full [Default]: calculating the pulse area by looping over all the pmt hits 107 | // Parametric: calculating the pulse area by using a parametric equation 108 | @@ -29,32 +29,32 @@ NEST::S2CalculationMode s2CalculationMode = NEST::S2CalculationMode::Full; 109 | // WaveformWithEtrain: calculate the pulse area and the waveform with etrain 110 | 111 | // 0 means PE, 1 means phd (PE/~1.2), 2 means spike count 112 | -int usePD = 2; 113 | +int usePD = 1; 114 | // band style: log(S2) with 1, while 0 means log(S2/S1) 115 | int useS2 = 0; // xtra feature: 2 means S2 x-axis energy scale 116 | 117 | double minS1 = 1.5; // units are controlled by the usePD flag 118 | // this is separate from S1 thresholds controlled by detector 119 | -double maxS1 = 99.5; 120 | -int numBins = 98; // for LUXRun03 DD, change these to 1.7,110.6,99 121 | +double maxS1 = 80.5; 122 | +int numBins = 79; // for LUXRun03 DD, change these to 1.7,110.6,99 123 | 124 | // for efficiency calculation 125 | // minS2 need not match S2 threshold in detector.hh 126 | // you can treat as trigger vs. analysis thresholds 127 | -double minS2 = 42.; 128 | -double maxS2 = 1e4; // 5e3 for DD. At least 2e5 for post-Run04 14C 129 | +double minS2 = 0.0; 130 | +double maxS2 = 1e9; // 5e3 for DD. At least 2e5 for post-Run04 14C 131 | 132 | // log(S2/S1) or log(S2) admitted into analysis incl. limit 133 | -double logMax = 3.6; // when skewness=1 or 2 ROOT ignores these and does raw 134 | +double logMax = 13.6; // when skewness=1 or 2 ROOT ignores these and does raw 135 | // mean +/- 3-sigma 136 | -double logMin = 0.6; 137 | -int logBins = 30; //#bins in between logMin & logMax for fits 138 | +double logMin = -0.6; 139 | +int logBins = 50; //#bins in between logMin & logMax for fits 140 | 141 | // some numbers for fine-tuning the speed vs. the accuracy 142 | double z_step = 143 | 0.1; // mm, for integrating non-uni EF. Larger detectors require larger 144 | // z_step ~0.5-1mm for tonne-scale TPCs like LZ and XENONnT 145 | -double E_step = 5.0; // keV, for integrating WIMP spectrum. NEST will warn you 146 | +double E_step = 2.5; // keV, for integrating WIMP spectrum. NEST will warn you 147 | // if choice poor 148 | // Rec >~20GeV 6keV, <~5GeV 0.5keV 149 | 150 | @@ -62,7 +62,7 @@ double E_step = 5.0; // keV, for integrating WIMP spectrum. NEST will warn you 151 | int freeParam = 152 | 2; // #free param for calculating DoF in X^2; 2 for Ly and Qy, or g1 and g2 153 | int skewness = 154 | - 1; // 1 means skew-Gaussian fits (2 more rigorous fit, more detail output) 155 | + 0; // 1 means skew-Gaussian fits (2 more rigorous fit, more detail output) 156 | int mode = 0; 157 | // 0 default is to provide 1 band (no data comp) or if 2 args ER BG discrim & 158 | // leakage frac 1 outputs GoF for sim band 1st cf. data band 2nd (Gauss centroids 159 | diff --git a/src/NEST.cpp b/src/NEST.cpp 160 | index 36198e3..b525ab9 100644 161 | --- a/src/NEST.cpp 162 | +++ b/src/NEST.cpp 163 | @@ -336,8 +336,8 @@ QuantaResult NESTcalc::GetQuanta(const YieldResult &yields, double density, cons 164 | // if ( !fdetector->get_OldW13eV() ) Variance /= sqrt ( ZurichEXOQ ); 165 | 166 | double skewness; 167 | - if ((yields.PhotonYield + yields.ElectronYield) > 1e4 || 168 | - yields.ElectricField > 4e3 || yields.ElectricField < 50.) { 169 | + if ((yields.PhotonYield + yields.ElectronYield) > 1e10 || 170 | + yields.ElectricField > 9e3 || yields.ElectricField < 0.) { 171 | skewness = 0.00; // make it a constant 0 when outside the range of Vetri 172 | // Velan's Run04 models. 173 | } else { 174 | -------------------------------------------------------------------------------- /utils/larnest/data/nr_light_yield.csv: -------------------------------------------------------------------------------- 1 | dataset,energy,energy_sl,energy_sh,field,field_sl,field_sh,yield,yield_sl,yield_sh,l_eff,l_eff_sl,l_eff_sh,converted,excluded 2 | SCENE 2015,10.3,1.4,1.5,0,0,0,11.3,0.53,0.53,0.235,0.011,0.011,0,0 3 | SCENE 2015,14.8,2.6,2.7,0,0,0,11.49,0.63,0.63,0.239,0.013,0.013,0,0 4 | SCENE 2015,16.9,1.5,1.5,0,0,0,11.25,0.48,0.48,0.234,0.01,0.01,0,0 5 | SCENE 2015,20.5,2.8,3,0,0,0,12.36,0.48,0.48,0.257,0.01,0.01,0,0 6 | SCENE 2015,25.4,2.9,3.2,0,0,0,12.07,0.53,0.53,0.251,0.011,0.011,0,0 7 | SCENE 2015,28.7,2.8,2.8,0,0,0,12.69,0.43,0.43,0.264,0.009,0.009,0,0 8 | SCENE 2015,36.1,3.1,3.1,0,0,0,13.37,0.48,0.48,0.278,0.01,0.01,0,0 9 | SCENE 2015,49.7,3.4,3.4,0,0,0,13.99,0.43,0.43,0.291,0.009,0.009,0,0 10 | SCENE 2015,57.3,4.9,5,0,0,0,14.18,0.48,0.48,0.295,0.01,0.01,0,0 11 | SCENE 2015,10.3,1.4,1.5,96.4,0,0,10.72,0.48,0.48,0.223,0.01,0.01,0,0 12 | SCENE 2015,14.8,2.6,2.7,96.4,0,0,11.2,0.53,0.53,0.233,0.011,0.011,0,0 13 | SCENE 2015,16.9,1.5,1.5,96.4,0,0,10.1,0.43,0.43,0.21,0.009,0.009,0,0 14 | SCENE 2015,20.5,2.8,3,96.4,0,0,11.68,0.48,0.48,0.243,0.01,0.01,0,0 15 | SCENE 2015,25.4,2.9,3.2,96.4,0,0,10.96,0.53,0.53,0.228,0.011,0.011,0,0 16 | SCENE 2015,28.7,2.8,2.8,96.4,0,0,12.79,0.38,0.38,0.266,0.008,0.008,0,0 17 | SCENE 2015,36.1,3.1,3.1,96.4,0,0,13.08,0.48,0.48,0.272,0.01,0.01,0,0 18 | SCENE 2015,49.7,3.4,3.4,96.4,0,0,13.99,0.48,0.48,0.291,0.01,0.01,0,0 19 | SCENE 2015,57.3,4.9,5,96.4,0,0,13.61,0.43,0.43,0.283,0.009,0.009,0,0 20 | SCENE 2015,16.9,1.5,1.5,193,0,0,9.71,0.38,0.38,0.202,0.008,0.008,0,0 21 | SCENE 2015,20.5,2.8,3,193,0,0,10.91,0.43,0.43,0.227,0.009,0.009,0,0 22 | SCENE 2015,25.4,2.9,3.2,193,0,0,10.72,0.43,0.43,0.223,0.009,0.009,0,0 23 | SCENE 2015,36.1,3.1,3.1,193,0,0,12.74,0.48,0.48,0.265,0.01,0.01,0,0 24 | SCENE 2015,57.3,4.9,5,193,0,0,13.56,0.58,0.58,0.282,0.012,0.012,0,0 25 | SCENE 2015,10.3,1.4,1.5,293,0,0,8.89,0.58,0.58,0.185,0.012,0.012,0,0 26 | SCENE 2015,14.8,2.6,2.7,293,0,0,9.81,0.48,0.48,0.204,0.01,0.01,0,0 27 | SCENE 2015,16.9,1.5,1.5,293,0,0,9.09,0.48,0.48,0.189,0.01,0.01,0,0 28 | SCENE 2015,20.5,2.8,3,293,0,0,10.48,0.43,0.43,0.218,0.009,0.009,0,0 29 | SCENE 2015,25.4,2.9,3.2,293,0,0,10.1,0.53,0.53,0.21,0.011,0.011,0,0 30 | SCENE 2015,28.7,2.8,2.8,293,0,0,11.39,0.43,0.43,0.237,0.009,0.009,0,0 31 | SCENE 2015,36.1,3.1,3.1,293,0,0,12.07,0.77,0.77,0.251,0.016,0.016,0,0 32 | SCENE 2015,49.7,3.4,3.4,293,0,0,12.88,0.38,0.38,0.268,0.008,0.008,0,0 33 | SCENE 2015,57.3,4.9,5,293,0,0,13.13,0.53,0.53,0.273,0.011,0.011,0,0 34 | SCENE 2015,10.3,1.4,1.5,970,0,0,7.64,0.53,0.53,0.159,0.011,0.011,0,0 35 | SCENE 2015,14.8,2.6,2.7,970,0,0,8.08,0.48,0.48,0.168,0.01,0.01,0,0 36 | SCENE 2015,20.5,2.8,3,970,0,0,8.99,0.38,0.38,0.187,0.008,0.008,0,0 37 | SCENE 2015,25.4,2.9,3.2,970,0,0,9.28,0.58,0.58,0.193,0.012,0.012,0,0 38 | SCENE 2015,28.7,2.8,2.8,970,0,0,9.66,0.48,0.48,0.201,0.01,0.01,0,0 39 | SCENE 2015,36.1,3.1,3.1,970,0,0,10.91,0.53,0.53,0.227,0.011,0.011,0,0 40 | SCENE 2015,49.7,3.4,3.4,970,0,0,11.88,0.48,0.48,0.247,0.01,0.01,0,0 41 | SCENE 2015,57.3,4.9,5,970,0,0,11.63,0.43,0.43,0.242,0.009,0.009,0,0 42 | MicroCLEAN 2012,11,1.2,1.2,0,0,0,19.7,3.8,4.8,0.41,0.08,0.10,0,0 43 | MicroCLEAN 2012,14,1.4,1.4,0,0,0,16.8,2.8,3.4,0.35,0.06,0.07,0,0 44 | MicroCLEAN 2012,19,1.59,1.59,0,0,0,13.5,1.9,2.4,0.28,0.04,0.05,0,0 45 | MicroCLEAN 2012,22,1.73,1.73,0,0,0,12.5,1.4,1.4,0.26,0.03,0.03,0,0 46 | MicroCLEAN 2012,28,1.91,1.91,0,0,0,11.1,1.4,1.4,0.23,0.03,0.03,0,0 47 | MicroCLEAN 2012,33,2.04,2.04,0,0,0,12,1,0.9,0.25,0.02,0.02,0,0 48 | MicroCLEAN 2012,39,2.2,2.2,0,0,0,12.5,1.4,0.9,0.26,0.03,0.02,0,0 49 | MicroCLEAN 2012,44,2.31,2.31,0,0,0,12.5,0.9,0.9,0.26,0.02,0.02,0,0 50 | MicroCLEAN 2012,52,2.45,2.45,0,0,0,13,0.9,0.9,0.27,0.02,0.02,0,0 51 | MicroCLEAN 2012,59,2.57,2.57,0,0,0,11.5,1,0.9,0.24,0.02,0.02,0,0 52 | MicroCLEAN 2012,67,2.68,2.68,0,0,0,11.1,1,1,0.23,0.02,0.02,0,0 53 | MicroCLEAN 2012,72,2.76,2.76,0,0,0,12,1,0.9,0.25,0.02,0.02,0,0 54 | MicroCLEAN 2012,79,2.82,2.82,0,0,0,11.5,1,0.9,0.24,0.02,0.02,0,0 55 | MicroCLEAN 2012,91,2.92,2.92,0,0,0,11.1,0.9,1,0.23,0.02,0.02,0,0 56 | MicroCLEAN 2012,98,2.96,2.96,0,0,0,11.5,1,0.9,0.24,0.02,0.02,0,0 57 | MicroCLEAN 2012,114,3.03,3.03,0,0,0,13.5,1.9,1.4,0.28,0.04,0.03,0,0 58 | MicroCLEAN 2012,191,2.73,2.73,0,0,0,13.9,1,1,0.29,0.02,0.02,0,0 59 | MicroCLEAN 2012,211,2.43,2.43,0,0,0,12.5,0.9,1,0.26,0.02,0.02,0,0 60 | MicroCLEAN 2012,239,1.8,1.8,0,0,0,12,0.5,0.9,0.25,0.01,0.02,0,0 61 | Creus 2015,11.5,2.8,2.8,0,0,0,18.6,1.5,1.5,0.386,0.032,0.032,0,0 62 | Creus 2015,16.4,3.9,3.9,0,0,0,14.7,1,1,0.305,0.020,0.020,0,0 63 | Creus 2015,28.5,5.2,5.2,0,0,0,13.7,0.7,0.7,0.285,0.014,0.014,0,0 64 | Creus 2015,43.4,6,6,0,0,0,14.1,1.1,1.1,0.294,0.023,0.023,0,0 65 | Creus 2015,60.5,7.1,7.1,0,0,0,13.6,0.9,0.9,0.283,0.018,0.018,0,0 66 | Creus 2015,119.6,7.7,7.7,0,0,0,14.5,0.9,0.9,0.301,0.019,0.019,0,0 67 | ARIS 2018,7.1,0.18,0.18,0,0,0,11.7,0.4,0.4,0.243,0.009,0.009,0,0 68 | ARIS 2018,13.7,0.34,0.34,0,0,0,12.4,0.3,0.3,0.258,0.007,0.007,0,0 69 | ARIS 2018,17.8,0.98,0.98,0,0,0,12.2,0.7,0.7,0.253,0.015,0.015,0,0 70 | ARIS 2018,21.7,0.54,0.54,0,0,0,12.9,0.3,0.3,0.269,0.006,0.006,0,0 71 | ARIS 2018,40.5,1.01,1.01,0,0,0,13.8,0.3,0.3,0.286,0.006,0.006,0,0 72 | ARIS 2018,65.4,1.64,1.64,0,0,0,14.6,0.4,0.4,0.304,0.008,0.008,0,0 73 | ARIS 2018,98.1,2.45,2.45,0,0,0,16,0.3,0.3,0.332,0.006,0.006,0,0 74 | ARIS 2018,117.8,2.95,2.95,0,0,0,16.8,0.2,0.2,0.349,0.005,0.005,0,0 75 | ARIS 2018,7.1,0.18,0.18,50,0,0,10.3,0.6,0.6,0.214,0.012,0.012,0,0 76 | ARIS 2018,13.7,0.34,0.34,50,0,0,11.2,0.5,0.5,0.233,0.010,0.010,0,0 77 | ARIS 2018,17.8,0.98,0.98,50,0,0,11.7,0.7,0.7,0.243,0.015,0.015,0,0 78 | ARIS 2018,21.7,0.54,0.54,50,0,0,12.3,0.4,0.4,0.256,0.008,0.008,0,0 79 | ARIS 2018,40.5,1.01,1.01,50,0,0,13.4,0.4,0.4,0.279,0.009,0.009,0,0 80 | ARIS 2018,65.4,1.64,1.64,50,0,0,14.3,0.6,0.6,0.298,0.012,0.012,0,0 81 | ARIS 2018,98.1,2.45,2.45,50,0,0,16,0.5,0.5,0.334,0.010,0.010,0,0 82 | ARIS 2018,117.8,2.95,2.95,50,0,0,16.7,0.4,0.4,0.347,0.008,0.008,0,0 83 | ARIS 2018,7.1,0.18,0.18,100,0,0,10.1,0.6,0.6,0.211,0.012,0.012,0,0 84 | ARIS 2018,13.7,0.34,0.34,100,0,0,11,0.7,0.7,0.228,0.014,0.014,0,0 85 | ARIS 2018,17.8,0.98,0.98,100,0,0,10.9,0.7,0.7,0.227,0.015,0.015,0,0 86 | ARIS 2018,21.7,0.54,0.54,100,0,0,11.6,0.4,0.4,0.242,0.008,0.008,0,0 87 | ARIS 2018,40.5,1.01,1.01,100,0,0,13.1,0.4,0.4,0.272,0.009,0.009,0,0 88 | ARIS 2018,65.4,1.64,1.64,100,0,0,14,0.6,0.6,0.291,0.013,0.013,0,0 89 | ARIS 2018,98.1,2.45,2.45,100,0,0,15.2,0.6,0.6,0.316,0.013,0.013,0,0 90 | ARIS 2018,117.8,2.95,2.95,100,0,0,16.3,0.4,0.4,0.338,0.009,0.009,0,0 91 | ARIS 2018,7.1,0.18,0.18,200,0,0,9.3,0.5,0.5,0.194,0.011,0.011,0,0 92 | ARIS 2018,13.7,0.34,0.34,200,0,0,10.5,0.5,0.5,0.218,0.010,0.010,0,0 93 | ARIS 2018,17.8,0.98,0.98,200,0,0,10.5,0.7,0.7,0.219,0.014,0.014,0,0 94 | ARIS 2018,21.7,0.54,0.54,200,0,0,11.2,0.3,0.3,0.233,0.007,0.007,0,0 95 | ARIS 2018,40.5,1.01,1.01,200,0,0,12.8,0.4,0.4,0.267,0.008,0.008,0,0 96 | ARIS 2018,65.4,1.64,1.64,200,0,0,13.9,0.5,0.5,0.290,0.011,0.011,0,0 97 | ARIS 2018,98.1,2.45,2.45,200,0,0,15.6,0.4,0.4,0.324,0.008,0.008,0,0 98 | ARIS 2018,117.8,2.95,2.95,200,0,0,16.3,0.4,0.4,0.340,0.008,0.008,0,0 99 | ARIS 2018,13.7,0.34,0.34,500,0,0,9.2,0.4,0.4,0.192,0.007,0.007,0,0 100 | ARIS 2018,17.8,0.98,0.98,500,0,0,9.5,0.6,0.6,0.197,0.012,0.012,0,0 101 | ARIS 2018,21.7,0.54,0.54,500,0,0,10,0.3,0.3,0.207,0.006,0.006,0,0 102 | ARIS 2018,40.5,1.01,1.01,500,0,0,11.6,0.3,0.3,0.242,0.007,0.007,0,0 103 | ARIS 2018,65.4,1.64,1.64,500,0,0,12.6,0.7,0.7,0.263,0.014,0.014,0,0 104 | ARIS 2018,98.1,2.45,2.45,500,0,0,14.4,0.4,0.4,0.299,0.007,0.007,0,0 105 | ARIS 2018,117.8,2.95,2.95,500,0,0,15.8,0.4,0.4,0.330,0.007,0.007,0,0 106 | Regenfus 2012,119,10.9,10.8,0,0,0,14.1,2,2,0.294,0.042,0.042,0,0 107 | Regenfus 2012,60,9.1,9.9,0,0,0,13.3,1.4,1.4,0.278,0.029,0.029,0,0 108 | Regenfus 2012,43,8,8.6,0,0,0,13.9,1.4,1.4,0.288,0.030,0.030,0,0 109 | Regenfus 2012,28,6.8,7.3,0,0,0,12.9,1.6,1.6,0.268,0.034,0.034,0,0 110 | Regenfus 2012,16,5.3,5.7,0,0,0,15.2,2,2,0.315,0.042,0.042,0,0 111 | WARP 2005,64.9,0,0,0,0,0,13.4,1.3,1.3,0.279,0.028,0.028,0,0 112 | SCENE 2013,10.8,0,0,1000,0,0,7.66,0.383,0.383,0.678,0.012,0.012,0,0 113 | SCENE 2013,10.8,0,0,300,0,0,8.775,0.463,0.463,0.777,0.019,0.019,0,0 114 | SCENE 2013,10.8,0,0,100,0,0,9.915,0.526,0.526,0.878,0.022,0.022,0,0 115 | SCENE 2013,15.2,0,0,1000,0,0,7.99,0.468,0.468,0.695,0.015,0.015,0,0 116 | SCENE 2013,15.2,0,0,300,0,0,9.592,0.557,0.557,0.835,0.017,0.017,0,0 117 | SCENE 2013,15.2,0,0,100,0,0,10.905,0.636,0.636,0.949,0.020,0.020,0,0 118 | SCENE 2013,15.2,0,0,50,0,0,10.868,0.643,0.643,0.946,0.022,0.022,0,0 119 | SCENE 2013,20.8,0,0,1000,0,0,9.172,0.362,0.362,0.742,0.005,0.005,0,0 120 | SCENE 2013,20.8,0,0,300,0,0,10.386,0.409,0.409,0.841,0.005,0.005,0,0 121 | SCENE 2013,20.8,0,0,200,0,0,11.009,0.437,0.437,0.891,0.007,0.007,0,0 122 | SCENE 2013,20.8,0,0,100,0,0,11.642,0.461,0.461,0.942,0.007,0.007,0,0 123 | SCENE 2013,20.8,0,0,50,0,0,11.44,0.481,0.481,0.979,0.008,0.008,0,0 124 | SCENE 2013,20.8,0,0,20,0,0,11.728,0.493,0.493,1.004,0.008,0.008,0,0 125 | SCENE 2013,29,0,0,1000,0,0,9.83,0.42,0.42,0.775,0.020,0.020,0,0 126 | SCENE 2013,29,0,0,300,0,0,11.346,0.525,0.525,0.894,0.028,0.028,0,0 127 | SCENE 2013,29,0,0,100,0,0,12.714,0.585,0.585,1.002,0.031,0.031,0,0 128 | SCENE 2013,29,0,0,50,0,0,13.005,0.538,0.538,1.025,0.024,0.024,0,0 129 | SCENE 2013,49.9,0,0,1000,0,0,12.064,0.511,0.511,0.862,0.025,0.025,0,0 130 | SCENE 2013,49.9,0,0,300,0,0,13.095,0.563,0.563,0.936,0.028,0.028,0,0 131 | SCENE 2013,49.9,0,0,100,0,0,13.786,0.589,0.589,0.985,0.029,0.029,0,0 -------------------------------------------------------------------------------- /include/Detectors/Xed_CWRU_Dahl.hh: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DetectorExample_XED_hh 3 | #define DetectorExample_XED_hh 1 4 | 5 | #include "VDetector.hh" 6 | #include 7 | 8 | using namespace std; 9 | 10 | class DetectorExample_XED : public VDetector { 11 | public: 12 | DetectorExample_XED() { 13 | // Call the initialization of all the parameters 14 | Initialization(); 15 | }; 16 | ~DetectorExample_XED() override = default; 17 | 18 | // Do here the initialization of all the parameters that are not varying as a 19 | // function of time 20 | virtual void Initialization() override { 21 | name = "Xed"; 22 | // Primary Scintillation (S1) parameters 23 | g1 = 0.05; // phd per S1 phot at dtCntr (not phe). Divide out 2-PE effect 24 | // 0.025 for Xed phase-I (Nishat Parveen, LZ, UAlbany: for Pb-206 work) 25 | sPEres = 0.44; // single phe resolution (Gaussian assumed) 26 | sPEthr = 0.4; // POD threshold in phe, usually used IN PLACE of sPEeff 27 | sPEeff = 1.0; // actual efficiency, can be used in lieu of POD threshold 28 | noiseBaseline[0] = 0.0; // baseline noise mean and width in PE (Gaussian) 29 | noiseBaseline[1] = 0.0; // baseline noise mean and width in PE (Gaussian) 30 | noiseBaseline[2] = 0.0; 31 | noiseBaseline[3] = 0.0; 32 | P_dphe = 0.2; // chance 1 photon makes 2 phe instead of 1 in Hamamatsu PMT 33 | 34 | coinWind = 100; // S1 coincidence window in ns 35 | coinLevel = 1; // how many PMTs have to fire for an S1 to count 36 | numPMTs = 4; // For coincidence calculation 37 | 38 | OldW13eV = true; 39 | noiseLinear[0] = 0.0312; 40 | noiseLinear[1] = 0.0395; 41 | 42 | // Ionization and Secondary Scintillation (S2) parameters 43 | g1_gas = 0.045; // phd per S2 photon in gas, used to get SE size 44 | s2Fano = 4.; // Fano-like fudge factor for SE width 45 | s2_thr = 35.; // the S2 threshold in phe or PE, *not* phd. Affects NR most 46 | // S2botTotRatio = 0.5; //bottom-to-total, typically only used for position 47 | // recon (1-this) 48 | E_gas = 10.; // field in kV/cm between liquid/gas border and anode 49 | eLife_us = 10.; // the drift electron mean lifetime in micro-seconds 50 | 51 | // Thermodynamic Properties 52 | inGas = false; 53 | T_Kelvin = 180.; // for liquid drift speed calculation 54 | p_bar = 2.3; // gas pressure in units of bars, it controls S2 size 55 | // if you are getting warnings about being in gas, lower T and/or raise p 56 | 57 | // Data Analysis Parameters and Geometry 58 | dtCntr = 2.5; // center of detector for S1 corrections, in usec. 59 | dt_min = 0.0; // minimum. Top of detector fiducial volume 60 | dt_max = 3.6; // maximum. Bottom of detector fiducial volume 61 | 62 | radius = 10.; // millimeters 63 | radmax = 10.; 64 | 65 | TopDrift = 10.; // mm not cm or us (but, this *is* where dt=0) 66 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 67 | // PMTs) In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, 68 | // not anode! 69 | anode = 13.; // the level of the anode grid-wire plane in mm 70 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 71 | // above it 72 | gate = 8.0; // mm. This is where the E-field changes (higher) 73 | // in gas detectors, the gate is still the gate, but it's where 74 | // S2 starts 75 | cathode = 0.; // mm. Defines point below which events are gamma-X 76 | 77 | // 2-D (X & Y) Position Reconstruction 78 | PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 79 | PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 80 | } 81 | 82 | // S1 PDE custom fit for function of z 83 | // s1polA + s1polB*z[mm] + s1polC*z^2+... (QE included, for binom dist) e.g. 84 | virtual double FitS1(double xPos_mm, double yPos_mm, double zPos_mm, 85 | LCE map) override { 86 | return 1.; // unitless, 1.000 at detector center 87 | } 88 | 89 | // Drift electric field as function of Z in mm 90 | // For example, use a high-order poly spline 91 | virtual double FitEF(double xPos_mm, double yPos_mm, 92 | double zPos_mm) override { // in V/cm 93 | uniform_int_distribution distribution(0, 6); 94 | unsigned seed = std::chrono::system_clock::now().time_since_epoch().count(); 95 | default_random_engine generator(seed); 96 | int switcher = distribution(generator); 97 | double electricField; 98 | if (switcher == 0) 99 | electricField = 0.10; 100 | else if (switcher == 1) 101 | electricField = 60.0; // or, 100?? 102 | else if (switcher == 2) 103 | electricField = 522.; 104 | else if (switcher == 3) 105 | electricField = 876.; 106 | else if (switcher == 4) 107 | electricField = 1951; 108 | else if (switcher == 5) 109 | electricField = 4060; 110 | else 111 | electricField = 4500; 112 | if (electricField < FIELD_MIN) electricField = FIELD_MIN; 113 | return electricField; 114 | } 115 | 116 | // S2 PDE custom fit for function of r 117 | // s2polA + s2polB*r[mm] + s2polC*r^2+... (QE included, for binom dist) e.g. 118 | virtual double FitS2(double xPos_mm, double yPos_mm, LCE map) override { 119 | return 1.; // unitless, 1.000 at detector center 120 | } 121 | 122 | virtual vector FitTBA(double xPos_mm, double yPos_mm, 123 | double zPos_mm) override { 124 | vector BotTotRat(2); 125 | 126 | BotTotRat[0] = 0.2; // S1 bottom-to-total ratio 127 | BotTotRat[1] = 0.8; // S2 bottom-to-total ratio, typically only used for 128 | // position reconstruction (1-this) 129 | return BotTotRat; 130 | } 131 | 132 | virtual double OptTrans(double xPos_mm, double yPos_mm, 133 | double zPos_mm) override { 134 | double phoTravT, approxCenter = (TopDrift + cathode) / 2., 135 | relativeZ = zPos_mm - approxCenter; 136 | 137 | double A = 0.048467 - 7.6386e-6 * relativeZ + 138 | 1.2016e-6 * pow(relativeZ, 2.) - 6.0833e-9 * pow(relativeZ, 3.); 139 | if (A < 0.) A = 0.; // cannot have negative probability 140 | double B_a = 0.99373 + 0.0010309 * relativeZ - 141 | 2.5788e-6 * pow(relativeZ, 2.) - 142 | 1.2000e-8 * pow(relativeZ, 3.); 143 | double B_b = 1. - B_a; 144 | double tau_a = 11.15; // all times in nanoseconds 145 | double tau_b = 4.5093 + 0.03437 * relativeZ - 146 | 0.00018406 * pow(relativeZ, 2.) - 147 | 1.6383e-6 * pow(relativeZ, 3.); 148 | if (tau_b < 0.) tau_b = 0.; // cannot have negative time 149 | 150 | // A = 0.0574; B_a = 1.062; tau_a = 11.1; tau_b = 2.70; B_b = 1.0 - B_a; 151 | // //LUX D-D conditions 152 | 153 | if (RandomGen::rndm()->rand_uniform() < A) 154 | phoTravT = 0.; // direct travel time to PMTs (low) 155 | else { // using P0(t) = 156 | // A*delta(t)+(1-A)*[(B_a/tau_a)e^(-t/tau_a)+(B_b/tau_b)e^(-t/tau_b)] 157 | // LUX PSD paper, but should apply to all detectors w/ diff #'s 158 | if (RandomGen::rndm()->rand_uniform() < B_a) 159 | phoTravT = -tau_a * log(RandomGen::rndm()->rand_uniform()); 160 | else 161 | phoTravT = -tau_b * log(RandomGen::rndm()->rand_uniform()); 162 | } 163 | 164 | double sig = RandomGen::rndm()->rand_gauss( 165 | 3.84, .09, true); // includes stat unc but not syst 166 | phoTravT += RandomGen::rndm()->rand_gauss( 167 | 0.00, sig, 168 | false); // the overall width added to photon time spectra by the 169 | // effects in the electronics and the data reduction pipeline 170 | 171 | if (phoTravT > DBL_MAX) phoTravT = tau_a; 172 | if (phoTravT < -DBL_MAX) phoTravT = 0.000; 173 | 174 | return phoTravT; // this function follows LUX (arXiv:1802.06162) not Xe10 175 | // technically but tried to make general 176 | } 177 | 178 | virtual vector SinglePEWaveForm(double area, double t0) override { 179 | vector PEperBin; 180 | 181 | double threshold = PULSEHEIGHT; // photo-electrons 182 | double sigma = PULSE_WIDTH; // ns 183 | area *= 10. * (1. + threshold); 184 | double amplitude = area / (sigma * sqrt(2. * M_PI)), 185 | signal; // assumes perfect Gaussian 186 | 187 | double tStep1 = 188 | SAMPLE_SIZE / 1e2; // ns, make sure much smaller than sample size; used 189 | // to generate MC-true pulses essentially 190 | double tStep2 = 191 | SAMPLE_SIZE; // ns; 1 over digitization rate, 100 MHz assumed here 192 | 193 | double time = -5. * sigma; 194 | bool digitizeMe = false; 195 | while (true) { 196 | signal = amplitude * exp(-pow(time, 2.) / (2. * sigma * sigma)); 197 | if (signal < threshold) { 198 | if (digitizeMe) 199 | break; 200 | else 201 | ; // do nothing - goes down to advancing time block 202 | } else { 203 | if (digitizeMe) 204 | PEperBin.push_back(signal); 205 | else { 206 | if (RandomGen::rndm()->rand_uniform() < 2. * (tStep1 / tStep2)) { 207 | PEperBin.push_back(time + t0); 208 | PEperBin.push_back(signal); 209 | digitizeMe = true; 210 | } else { 211 | } 212 | } 213 | } 214 | if (digitizeMe) 215 | time += tStep2; 216 | else 217 | time += tStep1; 218 | if (time > 5. * sigma) break; 219 | } 220 | 221 | return PEperBin; 222 | } 223 | // Vary VDetector parameters through custom functions 224 | virtual void ExampleFunction() { set_g1(0.06); } 225 | }; 226 | 227 | #endif 228 | -------------------------------------------------------------------------------- /include/Detectors/DetectorExample_XENON10.hh: -------------------------------------------------------------------------------- 1 | // 2 | // DetectorExample_XENON10.hh 3 | // 4 | // Adapted from Quentin Riffard by Jacob Cutter, May 8, 2018 5 | // 6 | // This header file serves as a template for creating one's own VDetector class. 7 | // This will be ultimately used to customize the NEST detector parameters to 8 | // meet 9 | // individual/collaboration needs. 10 | // 11 | // Note that the detector parameters can also be varied throughout a run, etc. 12 | 13 | #ifndef DetectorExample_XENON10_hh 14 | #define DetectorExample_XENON10_hh 1 15 | 16 | #include "VDetector.hh" 17 | 18 | using namespace std; 19 | 20 | class DetectorExample_XENON10 : public VDetector { 21 | public: 22 | DetectorExample_XENON10() { 23 | //cerr << "*** Detector definition message ***" << endl; 24 | //cerr << "You are currently using the default XENON10 template detector." 25 | // << endl 26 | // << endl; 27 | 28 | // Call the initialisation of all the parameters 29 | Initialization(); 30 | }; 31 | ~DetectorExample_XENON10() override = default; 32 | 33 | // Do here the initialization of all the parameters that are not varying as a 34 | // function of time 35 | void Initialization() override { 36 | name = "XENON10"; 37 | // Primary Scintillation (S1) parameters 38 | g1 = 0.073; // phd per S1 phot at dtCntr (not phe). Divide out 2-PE effect 39 | sPEres = 0.58; // single phe resolution (Gaussian assumed) 40 | sPEthr = 0.35; // POD threshold in phe, usually used IN PLACE of sPEeff 41 | sPEeff = 1.00; // actual efficiency, can be used in lieu of POD threshold 42 | noiseBaseline[0] = 0.0; // baseline noise mean in PE (Gaussian) 43 | noiseBaseline[1] = 0.0; // baseline noise width in PE (Gaussian) 44 | noiseBaseline[2] = 0.0; // baseline noise mean in e- (for grid wires) 45 | noiseBaseline[3] = 0.0; // baseline noise width in e- (for grid wires) 46 | P_dphe = 0.2; // chance 1 photon makes 2 phe instead of 1 in Hamamatsu PMT 47 | 48 | coinWind = 100; // S1 coincidence window in ns 49 | coinLevel = 2; // how many PMTs have to fire for an S1 to count 50 | numPMTs = 89; // For coincidence calculation 51 | 52 | OldW13eV = true; // for matching EXO-200's W measurement 53 | // the "Linear noise" terms as defined in Dahl thesis and by Dan McK 54 | noiseLinear[0] = 3e-2; // S1->S1 Gaussian-smeared with noiseL[0]*S1 55 | noiseLinear[1] = 3e-2; // S2->S2 Gaussian-smeared with noiseL[1]*S2 56 | 57 | // Ionization and Secondary Scintillation (S2) parameters 58 | g1_gas = .0655; // phd per S2 photon in gas, used to get SE size 59 | s2Fano = 3.61; // Fano-like fudge factor for SE width 60 | s2_thr = 300.; // the S2 threshold in phe or PE, *not* phd. Affects NR most 61 | E_gas = 12.; // field in kV/cm between liquid/gas border and anode 62 | eLife_us = 2200.; // the drift electron mean lifetime in micro-seconds 63 | 64 | // Thermodynamic Properties 65 | inGas = false; 66 | T_Kelvin = 177.; // for liquid drift speed calculation 67 | p_bar = 2.14; // gas pressure in units of bars, it controls S2 size 68 | // if you are getting warnings about being in gas, lower T and/or raise p 69 | 70 | // Data Analysis Parameters and Geometry 71 | dtCntr = 40.; // center of detector for S1 corrections, in usec. 72 | dt_min = 20.; // minimum. Top of detector fiducial volume 73 | dt_max = 60.; // maximum. Bottom of detector fiducial volume 74 | 75 | radius = 50.; // millimeters (fiducial rad) 76 | radmax = 50.; // actual physical geo. limit 77 | 78 | TopDrift = 150.; // mm not cm or us (but, this *is* where dt=0) 79 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 80 | // PMTs) 81 | // In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, not 82 | // anode! 83 | anode = 152.5; // the level of the anode grid-wire plane in mm 84 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 85 | // above it 86 | gate = 147.5; // mm. This is where the E-field changes (higher) 87 | // in gas detectors, the gate is still the gate, but it's where S2 starts 88 | cathode = 1.00; // mm. Defines point below which events are gamma-X 89 | 90 | // 2-D (X & Y) Position Reconstruction 91 | PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 92 | PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 93 | } 94 | 95 | // S1 PDE custom fit for function of z 96 | // s1polA + s1polB*z[mm] + s1polC*z^2+... (QE included, for binom dist) e.g. 97 | double FitS1(double xPos_mm, double yPos_mm, double zPos_mm, 98 | LCE map) override { 99 | return 1.; // unitless, 1.000 at detector center 100 | } 101 | 102 | // Drift electric field as function of Z in mm 103 | // For example, use a high-order poly spline 104 | double FitEF(double xPos_mm, double yPos_mm, 105 | double zPos_mm) override { // in V/cm 106 | return 730.; // NOTE: if just const don't use -1 field option at run-time 107 | } 108 | 109 | // S2 PDE custom fit for function of r 110 | // s2polA + s2polB*r[mm] + s2polC*r^2+... (QE included, for binom dist) e.g. 111 | double FitS2(double xPos_mm, double yPos_mm, LCE map) override { 112 | return 1.; // unitless, 1.000 at detector center 113 | } 114 | 115 | vector FitTBA(double xPos_mm, double yPos_mm, 116 | double zPos_mm) override { 117 | vector BotTotRat(2); 118 | 119 | BotTotRat[0] = 0.6; // S1 bottom-to-total ratio 120 | BotTotRat[1] = 0.4; // S2 bottom-to-total ratio, typically only used for 121 | // position recon (1-this) 122 | 123 | return BotTotRat; 124 | } 125 | 126 | double OptTrans(double xPos_mm, double yPos_mm, double zPos_mm) override { 127 | double phoTravT, approxCenter = (TopDrift + cathode) / 2., 128 | relativeZ = zPos_mm - approxCenter; 129 | 130 | double A = 0.048467 - 7.6386e-6 * relativeZ + 131 | 1.2016e-6 * pow(relativeZ, 2.) - 6.0833e-9 * pow(relativeZ, 3.); 132 | if (A < 0.) A = 0.; // cannot have negative probability 133 | double B_a = 0.99373 + 0.0010309 * relativeZ - 134 | 2.5788e-6 * pow(relativeZ, 2.) - 135 | 1.2000e-8 * pow(relativeZ, 3.); 136 | double B_b = 1. - B_a; 137 | double tau_a = 11.15; // all times in nanoseconds 138 | double tau_b = 4.5093 + 0.03437 * relativeZ - 139 | 0.00018406 * pow(relativeZ, 2.) - 140 | 1.6383e-6 * pow(relativeZ, 3.); 141 | if (tau_b < 0.) tau_b = 0.; // cannot have negative time 142 | 143 | // A = 0.0574; B_a = 1.062; tau_a = 11.1; tau_b = 2.70; B_b = 1.0 - B_a; 144 | // //LUX D-D conditions 145 | 146 | if (RandomGen::rndm()->rand_uniform() < A) 147 | phoTravT = 0.; // direct travel time to PMTs (low) 148 | else { // using P0(t) = 149 | // A*delta(t)+(1-A)*[(B_a/tau_a)e^(-t/tau_a)+(B_b/tau_b)e^(-t/tau_b)] 150 | // LUX PSD paper, but should apply to all detectors w/ diff #'s 151 | if (RandomGen::rndm()->rand_uniform() < B_a) 152 | phoTravT = -tau_a * log(RandomGen::rndm()->rand_uniform()); 153 | else 154 | phoTravT = -tau_b * log(RandomGen::rndm()->rand_uniform()); 155 | } 156 | 157 | double sig = RandomGen::rndm()->rand_gauss( 158 | 3.84, .09, true); // includes stat unc but not syst 159 | phoTravT += RandomGen::rndm()->rand_gauss( 160 | 0.00, sig, 161 | false); // the overall width added to photon time spectra by the 162 | // effects in the electronics and the data reduction 163 | // pipeline 164 | 165 | if (phoTravT > DBL_MAX) phoTravT = tau_a; 166 | if (phoTravT < -DBL_MAX) phoTravT = 0.000; 167 | 168 | return phoTravT; // this function follows LUX (arXiv:1802.06162) not Xe10 169 | // technically but tried to make general 170 | } 171 | 172 | vector SinglePEWaveForm(double area, double t0) override { 173 | vector PEperBin; 174 | 175 | double threshold = PULSEHEIGHT; // photo-electrons 176 | double sigma = PULSE_WIDTH; // ns 177 | area *= 10. * (1. + threshold); 178 | double amplitude = area / (sigma * sqrt(2. * M_PI)), 179 | signal; // assumes perfect Gaussian 180 | 181 | double tStep1 = SAMPLE_SIZE / 1e2; // ns, make sure much smaller than 182 | // sample size; used to generate MC-true 183 | // pulses essentially 184 | double tStep2 = 185 | SAMPLE_SIZE; // ns; 1 over digitization rate, 100 MHz assumed here 186 | 187 | double time = -5. * sigma; 188 | bool digitizeMe = false; 189 | while (true) { 190 | signal = amplitude * exp(-pow(time, 2.) / (2. * sigma * sigma)); 191 | if (signal < threshold) { 192 | if (digitizeMe) 193 | break; 194 | else 195 | ; // do nothing - goes down to advancing time block 196 | } else { 197 | if (digitizeMe) 198 | PEperBin.push_back(signal); 199 | else { 200 | if (RandomGen::rndm()->rand_uniform() < 2. * (tStep1 / tStep2)) { 201 | PEperBin.push_back(time + t0); 202 | PEperBin.push_back(signal); 203 | digitizeMe = true; 204 | } else { 205 | } 206 | } 207 | } 208 | if (digitizeMe) 209 | time += tStep2; 210 | else 211 | time += tStep1; 212 | if (time > 5. * sigma) break; 213 | } 214 | 215 | return PEperBin; 216 | } 217 | // Vary VDetector parameters through custom functions 218 | virtual void ExampleFunction() { set_g1(0.0760); } 219 | virtual void ExampleFunction2() { set_molarMass(131.); } 220 | }; 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /include/Detectors/VDetector.hh: -------------------------------------------------------------------------------- 1 | // 2 | // VDetector.hh 3 | // 4 | // Adapted from Quentin Riffard by Jacob Cutter, May 8, 2018 5 | 6 | // ********************************************************************* 7 | // THIS DEFAULT VIRTUAL DETECTOR SHOULD ONLY BE MODIFIED BY DEVELOPERS. 8 | // PLEASE DEFINE YOUR OWN DETECTOR (see DetectorExample_XENON10.hh, or 9 | // the LUX Run03 detector header file: they each explain the units too). 10 | // ********************************************************************* 11 | 12 | #include 13 | #ifndef VDetector_hh 14 | #define VDetector_hh 1 15 | 16 | #include 17 | 18 | class VDetector { 19 | public: 20 | VDetector(); 21 | virtual ~VDetector(); 22 | virtual void Initialization(); 23 | 24 | // "Get Functions" 25 | std::string getName() const {return name;} 26 | // Primary Scintillation (S1) parameters 27 | double get_g1() const { return g1; } 28 | double get_sPEres() const { return sPEres; } 29 | double get_sPEthr() const { return sPEthr; } 30 | double get_sPEeff() const { return sPEeff; } 31 | const double* get_noiseBaseline() { return &noiseBaseline[0]; } 32 | const double* get_noiseLinear() { return &noiseLinear[0]; } 33 | const double* get_noiseQuadratic() { return &noiseQuadratic[0]; } 34 | double get_P_dphe() const { return P_dphe; } 35 | 36 | bool get_OldW13eV() const { return OldW13eV; } 37 | double get_coinWind() const { return coinWind; } 38 | int get_coinLevel() const { return coinLevel; } 39 | int get_numPMTs() const { return numPMTs; } 40 | 41 | // Ionization and Secondary Scintillation (S2) parameters 42 | double get_g1_gas() const { return g1_gas; } 43 | double get_s2Fano() const { return s2Fano; } 44 | double get_s2_thr() const { return s2_thr; } 45 | double get_E_gas() const { return E_gas; } 46 | double get_eLife_us() const { return eLife_us; } 47 | 48 | // Thermodynamic Properties 49 | bool get_inGas() const { return inGas; } 50 | double get_T_Kelvin() const { return T_Kelvin; } 51 | double get_p_bar() const { return p_bar; } 52 | 53 | // Data Analysis Parameters and Geometry 54 | double get_dtCntr() const { return dtCntr; } 55 | double get_dt_min() const { return dt_min; } 56 | double get_dt_max() const { return dt_max; } 57 | double get_radius() const { return radius; } 58 | double get_radmax() const { return radmax; } 59 | double get_TopDrift() const { return TopDrift; } 60 | double get_anode() const { return anode; } 61 | double get_cathode() const { return cathode; } 62 | double get_gate() const { return gate; } 63 | 64 | // 2-D (X & Y) Position Reconstruction 65 | double get_PosResExp() const { return PosResExp; } 66 | double get_PosResBase() const { return PosResBase; } 67 | 68 | // Xenon properties 69 | double get_molarMass() const { return molarMass; } 70 | 71 | // "Set Functions" 72 | // Primary Scintillation (S1) parameters 73 | void set_g1(double param) { g1 = param; } 74 | void set_sPEres(double param) { sPEres = param; } 75 | void set_sPEthr(double param) { sPEthr = param; } 76 | void set_sPEeff(double param) { sPEeff = param; } 77 | void set_noiseBaseline(double p1, double p2, double p3, double p4) { 78 | noiseBaseline[0] = p1; 79 | noiseBaseline[1] = p2; 80 | noiseBaseline[2] = p3; 81 | noiseBaseline[3] = p4; 82 | } 83 | void set_noiseLinear(double p1, double p2) { 84 | noiseLinear[0] = p1; 85 | noiseLinear[1] = p2; 86 | } 87 | void set_noiseQuadratic(double p1, double p2) { 88 | noiseQuadratic[0] = p1; 89 | noiseQuadratic[1] = p2; 90 | } 91 | void set_P_dphe(double param) { P_dphe = param; } 92 | 93 | void set_OldW13eV(bool param) { OldW13eV = param; } 94 | void set_coinWind(double param) { coinWind = param; } 95 | void set_coinLevel(int param) { coinLevel = param; } 96 | void set_numPMTs(int param) { numPMTs = param; } 97 | 98 | // Ionization and Secondary Scintillation (S2) parameters 99 | void set_g1_gas(double param) { g1_gas = param; } 100 | void set_s2Fano(double param) { s2Fano = param; } 101 | void set_s2_thr(double param) { s2_thr = param; } 102 | void set_E_gas(double param) { E_gas = param; } 103 | void set_eLife_us(double param) { eLife_us = param; } 104 | 105 | // Thermodynamic Properties 106 | void set_inGas(bool param) { inGas = param; } 107 | void set_T_Kelvin(double param) { T_Kelvin = param; } 108 | void set_p_bar(double param) { p_bar = param; } 109 | 110 | // Data Analysis Parameters and Geometry 111 | void set_dtCntr(double param) { dtCntr = param; } 112 | void set_dt_min(double param) { dt_min = param; } 113 | void set_dt_max(double param) { dt_max = param; } 114 | void set_radius(double param) { radius = param; } 115 | void set_radmax(double param) { radmax = param; } 116 | void set_TopDrift(double param) { TopDrift = param; } 117 | void set_anode(double param) { anode = param; } 118 | void set_cathode(double param) { cathode = param; } 119 | void set_gate(double param) { gate = param; } 120 | 121 | // 2-D (X & Y) Position Reconstruction 122 | void set_PosResExp(double param) { PosResExp = param; } 123 | void set_PosResBase(double param) { PosResBase = param; } 124 | 125 | // Xenon properties 126 | void set_molarMass(double param) { molarMass = param; } 127 | 128 | typedef enum { fold = 0, unfold = 1 } LCE; 129 | // S1 PDE custom fit for function of z 130 | // s1polA + s1polB*z[mm] + s1polC*z^2+... (QE included, for binom dist) e.g. 131 | virtual double FitS1(double, double, double, LCE) { return 1.; } 132 | 133 | // Drift electric field intensity as function of the point in mm 134 | // For example, use a high-order poly spline 135 | virtual double FitEF(double, double, double) { return 730.; } 136 | // Set a custom direction for the electric field (by default constant in Z) 137 | virtual std::vector FitDirEF(double, double, double) { 138 | return {0, 0, 1}; 139 | } 140 | 141 | // S2 PDE custom fit for function of r 142 | // s2polA + s2polB*r[mm] + s2polC*r^2+... (QE included, for binom dist) e.g. 143 | virtual double FitS2(double, double, LCE) { return 1.; } 144 | 145 | virtual std::vector FitTBA(double, double, double) { 146 | std::vector TopBotAsym = {0.5, 0.5}; 147 | return TopBotAsym; 148 | } 149 | 150 | virtual double OptTrans(double, double, double) { return 0.; } 151 | virtual std::vector SinglePEWaveForm(double, double) { 152 | std::vector PEperBin = {0.}; 153 | return PEperBin; 154 | } 155 | 156 | protected: 157 | std::string name = "VDetector"; 158 | // Primary Scintillation (S1) parameters 159 | double g1 = 160 | 0.0760; // phd per S1 phot at dtCntr (not phe). Divide out 2-PE effect 161 | double sPEres = 0.58; // single phe resolution (Gaussian assumed) 162 | double sPEthr = 163 | 0.35; // POD threshold in phe, usually used IN PLACE of sPEeff 164 | double sPEeff = 165 | 1.00; // actual efficiency, can be used in lieu of POD threshold 166 | double noiseBaseline[4] = { 167 | 0.0, // baseline noise mean and width in PE (Gaussian) 168 | 0.0, // baseline noise mean and width in PE (Gaussian) 169 | 0.0, // EXO noise mean 170 | 0.0 // EXO noise width 171 | }; 172 | 173 | double P_dphe = 174 | 0.2; // chance 1 photon makes 2 phe instead of 1 in Hamamatsu PMT 175 | 176 | double coinWind = 100; // S1 coincidence window in ns 177 | int coinLevel = 2; // how many PMTs have to fire for an S1 to count 178 | int numPMTs = 89; // For coincidence calculation 179 | 180 | bool OldW13eV = true; // for matching EXO-200's W measurement 181 | //"Linear noise" terms as defined in Dahl thesis and by D. McK 182 | double noiseLinear[2] = { 183 | 3e-2, 3e-2}; // S1->S1 Gaussian-smeared w/ noiseL[0]*S1. Ditto S2 184 | double noiseQuadratic[2] = {0.0, 0.0}; //(n)EXO quadratic noise term 185 | 186 | // Ionization and Secondary Scintillation (S2) parameters 187 | double g1_gas = 0.06; // phd per S2 photon in gas, used to get SE size 188 | double s2Fano = 3.61; // Fano-like fudge factor for SE width 189 | double s2_thr = 190 | 300.; // the S2 threshold in phe or PE, *not* phd. Affects NR most 191 | double E_gas = 12.; // field in kV/cm between liquid/gas border and anode 192 | double eLife_us = 2200.; // the drift electron mean lifetime in micro-seconds 193 | 194 | // Thermodynamic Properties 195 | bool inGas = false; 196 | double T_Kelvin = 177.; // for liquid drift speed calculation 197 | double p_bar = 2.14; // gas pressure in units of bars, it controls S2 size 198 | // if you are getting warnings about being in gas, lower T and/or raise p 199 | 200 | // Data Analysis Parameters and Geometry 201 | double dtCntr = 40.; // center of detector for S1 corrections, in usec. 202 | double dt_min = 20.; // minimum. Top of detector fiducial volume 203 | double dt_max = 60.; // maximum. Bottom of detector fiducial volume 204 | 205 | double radius = 50.; // millimeters 206 | double radmax = 50.; 207 | 208 | double TopDrift = 150.; // mm not cm or us (but, this *is* where dt=0) 209 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 210 | // PMTs) 211 | // In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, not 212 | // anode! 213 | double anode = 152.5; // the level of the anode grid-wire plane in mm 214 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 215 | // above it 216 | double gate = 147.5; // mm. This is where the E-field changes (higher) 217 | // in gas detectors, the gate is still the gate, but it's where S2 starts 218 | double cathode = 1.00; // mm. Defines point below which events are gamma-X 219 | 220 | // 2-D (X & Y) Position Reconstruction 221 | double PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 222 | double PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 223 | 224 | double molarMass = 131.293; // molar mass, g/mol 225 | }; 226 | #endif 227 | -------------------------------------------------------------------------------- /examples/multipleScatter.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: multipleScatter.cpp 3 | * Author: Greg Rischbeter 4 | * 5 | * Created on January 28, 2022 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "RandomGen.hh" 20 | #include "analysis.hh" 21 | 22 | using namespace std; 23 | 24 | int main(int argc, char **argv) { 25 | if (argc < 4) { 26 | cerr << "This program aims to take execNEST outputs, and combine energy " 27 | "deposits into Double Scatters. " 28 | << endl; 29 | cerr << endl 30 | << "Usage: " << endl 31 | << "./multipleScatter " 32 | " " 33 | << endl; 34 | 35 | cerr << " -- is the number of events to stitch together. " 36 | << endl; 37 | cerr << " -- is the number of energy deposits to combine; use " 38 | "-1 for a random exponential draw. " 39 | << endl; 40 | cerr << " -- is either 0 (for uncorrected pulses) or 1 (for " 41 | "corrected pulses); corrected pulses sum S2s and take an " 42 | "S2-weighted average S1c. " 43 | << endl; 44 | cerr << " -- is execNEST output events used to stitch together " 45 | "multiple scatters." 46 | << endl; 47 | cerr << " -- is optional, and will be the last scatter in each " 48 | "event, if provided." 49 | << endl; 50 | return 1; 51 | } 52 | // Load the number of events 53 | int nEvents = atoi(argv[1]); 54 | int nScatters = atoi(argv[2]); 55 | // RandomGen::rndm()->SetSeed(time(nullptr)); 56 | bool randomScatters = false; 57 | if (nScatters < 2) { 58 | randomScatters = true; 59 | } 60 | // define parameters for random exponential draw of nScatters 61 | // if using nScatters = -1. 62 | // These are just 0th order fill-ins to produce a spread of values. 63 | double halfLife = 1.; 64 | double minScatters = 1.5; // int-rounding will make the true minimum = 2 65 | double maxScatters = 50.; 66 | // Check if using corrected or uncorrected output pulse areas 67 | bool corrected = true; 68 | if (atoi(argv[3]) == 0) corrected = false; 69 | if (verbosity > 0) { 70 | if (corrected) 71 | cout << "S1c [phd]\tS2c [phd]\tnScatters" << endl; 72 | else 73 | cout << "S1 [phd]\tS2 [phd]\tnScatters" << endl; 74 | } 75 | bool sameFile = true; 76 | if (argc > 5) { 77 | sameFile = false; 78 | // cerr << "Using two files to generate scatters." << endl; 79 | } 80 | // open the first file and get the S1, S2, and their correction factors 81 | FILE *file1 = fopen(argv[4], "r"); 82 | double a, b, c, d, e, f, g, h, i, j, k, l, m, n; 83 | int ch, nLines = 0; 84 | vector S1a, S2a; 85 | vector S1a_corFactor, S2a_corFactor; 86 | if (verbosity > 0) { 87 | while (EOF != (ch = getc(file1))) { 88 | if ('\n' == ch && nLines) 89 | break; 90 | else 91 | nLines = 0; 92 | if (']' == ch && nLines == 0) ++nLines; 93 | } 94 | } 95 | while (1) { 96 | int scan = fscanf( 97 | file1, 98 | "%lf\t%lf\t%lf\t%lf,%lf,%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf", 99 | &a, &b, &c, &d, &e, &f, &g, &h, &i, &j, &k, &l, &m, &n); 100 | if (feof(file1)) break; 101 | // fprintf(stderr,"%.6f\t%.6f\t%.6f\t%.6f,%.6f,%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\n",a,b,c,d,e,f,g,h,i,j,k,l,m,n); 102 | if ((fabs(n) > 1.) && (fabs(m) > 1.)) { 103 | S1a.push_back(fabs(j)); 104 | S2a.push_back(fabs(m)); 105 | S1a_corFactor.push_back(fabs(k / j)); 106 | S2a_corFactor.push_back(fabs(n / m)); 107 | } 108 | } // Done with the first file.... 109 | fclose(file1); 110 | if (!sameFile) { 111 | cerr << "Using two files to generate scatters..." << endl; 112 | // Load the 2nd file if provided, and stitch the two together to make 113 | // multiple scatter events scatters 114 | FILE *file2 = fopen(argv[5], "r"); 115 | nLines = 0; 116 | double a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2; 117 | vector S1b, S2b; 118 | vector S1b_corFactor, S2b_corFactor; 119 | if (verbosity > 0) { 120 | while (EOF != (ch = getc(file2))) { 121 | if ('\n' == ch && nLines) 122 | break; 123 | else 124 | nLines = 0; 125 | if (']' == ch && nLines == 0) ++nLines; 126 | } 127 | } 128 | 129 | while (1) { 130 | int scan2 = fscanf( 131 | file2, 132 | "%lf\t%lf\t%lf\t%lf,%lf,%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf\t%lf", 133 | &a2, &b2, &c2, &d2, &e2, &f2, &g2, &h2, &i2, &j2, &k2, &l2, &m2, &n2); 134 | if (feof(file2)) break; 135 | // fprintf(stderr,"%.6f\t%.6f\t%.6f\t%.6f,%.6f,%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\t%.6f\n",a,b,c,d,e,f,g,h,i,j,k,l,m,n); 136 | if ((fabs(n2) > 1.) && 137 | (fabs(m2) > 1.)) { // prevent loading events where S2 ~ PHE_MIN 138 | S1b.push_back(fabs(j2)); // was causing memory leaks 139 | S2b.push_back(fabs(m2)); 140 | S1b_corFactor.push_back(fabs(k2 / j2)); 141 | S2b_corFactor.push_back(fabs(n2 / m2)); 142 | } 143 | } // Done with the second file 144 | fclose(file2); 145 | 146 | auto size1 = S1a.size(); 147 | auto size2 = S1b.size(); 148 | double totalS1, totalS2, totalCorFactor; 149 | double thisS1a, thisS1b, thisS2a, thisS2b; 150 | double thisS1a_corFactor, thisS2a_corFactor, thisS1b_corFactor, 151 | thisS2b_corFactor; 152 | for (int ii = 0; ii < nEvents; ii++) { 153 | while (nScatters < 2) { 154 | nScatters = // 0th order attempt at reproducing multiple scatter 155 | // behavior 156 | (int)(RandomGen::rndm()->rand_exponential(halfLife, minScatters, 157 | maxScatters) + 158 | 0.5); 159 | } 160 | totalS1 = 0.; 161 | totalCorFactor = 0.; 162 | totalS2 = 0.; 163 | for (int jj = 0; jj < nScatters - 1; jj++) { 164 | // randomly draw an energy deposit from the first file 165 | int index = RandomGen::rndm()->integer_range(0, (int)size1 - 1); 166 | 167 | thisS1a = S1a[index]; 168 | thisS2a = S2a[index]; 169 | thisS1a_corFactor = S1a_corFactor[index]; 170 | thisS2a_corFactor = S2a_corFactor[index]; 171 | if (corrected) { 172 | double cor_S2a = thisS2a * thisS2a_corFactor; 173 | totalS2 += cor_S2a; 174 | totalS1 += thisS1a; 175 | totalCorFactor += thisS1a_corFactor * cor_S2a; 176 | } else { 177 | totalS1 += thisS1a; 178 | totalS2 += thisS2a; 179 | } 180 | } // now on to the last scatter from the 2nd file 181 | int index2 = RandomGen::rndm()->integer_range(0, (int)size2 - 1); 182 | thisS1b = S1b[index2]; 183 | thisS2b = S2b[index2]; 184 | thisS1b_corFactor = S1b_corFactor[index2]; 185 | thisS2b_corFactor = S2b_corFactor[index2]; 186 | 187 | if (corrected) { 188 | double cor_S2b = thisS2b * thisS2b_corFactor; 189 | totalS2 += cor_S2b; 190 | totalS1 += thisS1b; 191 | totalCorFactor += thisS1b_corFactor * cor_S2b; 192 | totalS1 *= totalCorFactor / totalS2; 193 | } else { 194 | totalS1 += thisS1b; 195 | totalS2 += thisS2b; 196 | } 197 | cout << totalS1 << "\t" << totalS2 << "\t" << nScatters 198 | << endl; //"\t" << thisS1a << " " << thisS1b << " " << thisS2a << " 199 | //" << thisS2b << endl; 200 | if (randomScatters) nScatters = -1; 201 | } 202 | return 0; 203 | } else { 204 | cerr << "Generating multiple scatters from file..." << endl; 205 | // Drawing both scatters from the same file... 206 | int nGenerated = 0; 207 | 208 | auto size1 = S1a.size(); 209 | double totalS1, totalS2, totalCorFactor; 210 | double thisS1a, thisS1b, thisS2a, thisS2b; 211 | double thisS1a_corFactor, thisS2a_corFactor, thisS1b_corFactor, 212 | thisS2b_corFactor; 213 | 214 | while (nGenerated < nEvents) { 215 | while (nScatters < 2) { 216 | nScatters = // 0th order attempt at reproducing multiple scatter 217 | // behavior 218 | (int)(RandomGen::rndm()->rand_exponential(halfLife, minScatters, 219 | maxScatters) + 220 | 0.5); 221 | } 222 | totalS1 = 0.; 223 | totalCorFactor = 0.; 224 | totalS2 = 0.; 225 | for (int jj = 0; jj < nScatters; jj++) { 226 | // randomly draw an energy deposit from the first file 227 | int index = RandomGen::rndm()->integer_range(0, (int)size1 - 1); 228 | 229 | thisS1a = S1a[index]; 230 | thisS2a = S2a[index]; 231 | thisS1a_corFactor = S1a_corFactor[index]; 232 | thisS2a_corFactor = S2a_corFactor[index]; 233 | if (corrected) { 234 | double cor_S2a = thisS2a * thisS2a_corFactor; 235 | totalS2 += cor_S2a; 236 | totalS1 += thisS1a; 237 | totalCorFactor += thisS1a_corFactor * cor_S2a; 238 | } else { 239 | totalS1 += thisS1a; 240 | totalS2 += thisS2a; 241 | } 242 | } 243 | if (corrected) 244 | totalS1 *= totalCorFactor / totalS2; // S2c-weighted average corrected 245 | // S1 246 | 247 | cout << totalS1 << "\t" << totalS2 << "\t" << nScatters 248 | << endl; //"\t" << thisS1a << " " << thisS1b << " " << thisS2a << " 249 | //" << thisS2b << endl; 250 | nGenerated++; 251 | if (randomScatters) nScatters = -1; 252 | } 253 | return 0; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /include/Detectors/Z3_FSR_HA.hh: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DetectorExample_ZEPLIN_hh 3 | #define DetectorExample_ZEPLIN_hh 1 4 | 5 | #include "VDetector.hh" 6 | 7 | using namespace std; 8 | 9 | class DetectorExample_ZEPLIN : public VDetector { 10 | public: 11 | DetectorExample_ZEPLIN() { 12 | //cerr << "*** Detector definition message ***" << endl; 13 | //cerr << "You are currently using ZEPLIN-III First Science Run." << endl 14 | // << endl; 15 | 16 | // Call the initialisation of all the parameters 17 | Initialization(); 18 | }; 19 | ~DetectorExample_ZEPLIN() override = default; 20 | 21 | // Do here the initialisation of all the parameters that are not varying as a 22 | // function of time 23 | virtual void Initialization() override { 24 | name = "ZEPLIN-III First Science Run"; 25 | // Primary Scintillation (S1) parameters 26 | g1 = 0.0714; // phd per S1 phot at dtCntr (not phe). Error bar +/- 0.0050 27 | sPEres = 0.4; // single phe resolution (Gaussian assumed) 28 | sPEthr = 0.33; // POD threshold in phe, usually used IN PLACE of sPEeff 29 | sPEeff = 1.0; // actual efficiency, can be used in lieu of POD threshold 30 | noiseBaseline[0] = 0.0; // baseline noise mean and width in PE (Gaussian) 31 | noiseBaseline[1] = 0.0; // baseline noise mean and width in PE (Gaussian) 32 | noiseBaseline[2] = 0.0; 33 | noiseBaseline[3] = 0.0; 34 | P_dphe = 0.11; // chance 1 photon makes 2 phe instead of 1 in ETEL PMTs 35 | // (LopezParedes, 2018) 36 | 37 | coinWind = 50; // S1 coincidence window in ns (Note: Z3 used time 38 | // difference method in Neves, 2011) 39 | coinLevel = 3; // how many PMTs have to fire for an S1 to count 40 | numPMTs = 31; // For coincidence calculation using the nFold defined in 41 | // previous line 42 | 43 | OldW13eV = true; 44 | noiseLinear[0] = 0.; 45 | noiseLinear[1] = 0.; 46 | 47 | // Ionization and Secondary Scintillation (S2) parameters 48 | g1_gas = 0.111; // phd per S2 photon in gas, used to get SE size. ~4.25mm 79 | TopDrift = 35.75; // mm not cm or us (but, this *is* where dt=0) - NOTE: Z3 80 | // HAD NO GATE 81 | // a z-axis value of 0 means the bottom of the detector (cathode OR bottom 82 | // PMTs) 83 | // In 2-phase, TopDrift=liquid/gas border. In gas detector it's GATE, not 84 | // anode! 85 | anode = 40.0; // the level of the anode grid-wire plane in mm 86 | // In a gas TPC, this is not TopDrift (top of drift region), but a few mm 87 | // above it 88 | gate = 35.75; // mm. This is where the E-field changes (higher) - NOTE: Z3 89 | // HAD NO GATE 90 | // in gas detectors, the gate is still the gate, but it's where S2 starts 91 | cathode = 0.; // mm. Defines point below which events are gamma-X 92 | 93 | // 2-D (X & Y) Position Reconstruction (Solovov 2012) 94 | PosResExp = 0.015; // exp increase in pos recon res at hi r, 1/mm 95 | PosResBase = 70.8364; // baseline unc in mm, see NEST.cpp for usage 96 | } 97 | 98 | // S1 PDE custom fit for function of z 99 | // s1polA + s1polB*z[mm] + s1polC*z^2+... (QE included, for binom dist) e.g. 100 | virtual double FitS1(double xPos_mm, double yPos_mm, double zPos_mm, 101 | LCE map) override { 102 | return 1.; // unitless, 1.000 at detector center 103 | } 104 | 105 | // Drift electric field as function of Z in mm 106 | // For example, use a high-order poly spline 107 | virtual double FitEF(double xPos_mm, double yPos_mm, 108 | double zPos_mm) override { // in V/cm 109 | return 3840.; // compromise between 3900 (published) and 3800 (reanalysis) 110 | } 111 | 112 | // S2 PDE custom fit for function of r 113 | // s2polA + s2polB*r[mm] + s2polC*r^2+... (QE included, for binom dist) e.g. 114 | virtual double FitS2(double xPos_mm, double yPos_mm, LCE map) override { 115 | return 1.; // unitless, 1.000 at detector center 116 | } 117 | 118 | virtual vector FitTBA(double xPos_mm, double yPos_mm, 119 | double zPos_mm) override { 120 | vector BotTotRat(2); 121 | 122 | BotTotRat[0] = 0.6; // S1 bottom-to-total ratio 123 | BotTotRat[1] = 0.4; // S2 bottom-to-total ratio, typically only used for 124 | // position recon (1-this) 125 | 126 | return BotTotRat; 127 | } 128 | 129 | virtual double OptTrans(double xPos_mm, double yPos_mm, 130 | double zPos_mm) override { 131 | double phoTravT, approxCenter = (TopDrift + cathode) / 2., 132 | relativeZ = zPos_mm - approxCenter; 133 | 134 | double A = 0.048467 - 7.6386e-6 * relativeZ + 135 | 1.2016e-6 * pow(relativeZ, 2.) - 6.0833e-9 * pow(relativeZ, 3.); 136 | if (A < 0.) A = 0.; // cannot have negative probability 137 | double B_a = 0.99373 + 0.0010309 * relativeZ - 138 | 2.5788e-6 * pow(relativeZ, 2.) - 139 | 1.2000e-8 * pow(relativeZ, 3.); 140 | double B_b = 1. - B_a; 141 | double tau_a = 11.15; // all times in nanoseconds 142 | double tau_b = 4.5093 + 0.03437 * relativeZ - 143 | 0.00018406 * pow(relativeZ, 2.) - 144 | 1.6383e-6 * pow(relativeZ, 3.); 145 | if (tau_b < 0.) tau_b = 0.; // cannot have negative time 146 | 147 | // A = 0.0574; B_a = 1.062; tau_a = 11.1; tau_b = 2.70; B_b = 1.0 - B_a; 148 | // //LUX D-D conditions 149 | 150 | if (RandomGen::rndm()->rand_uniform() < A) 151 | phoTravT = 0.; // direct travel time to PMTs (low) 152 | else { // using P0(t) = 153 | // A*delta(t)+(1-A)*[(B_a/tau_a)e^(-t/tau_a)+(B_b/tau_b)e^(-t/tau_b)] 154 | // LUX PSD paper, but should apply to all detectors w/ diff #'s 155 | if (RandomGen::rndm()->rand_uniform() < B_a) 156 | phoTravT = -tau_a * log(RandomGen::rndm()->rand_uniform()); 157 | else 158 | phoTravT = -tau_b * log(RandomGen::rndm()->rand_uniform()); 159 | } 160 | 161 | double sig = RandomGen::rndm()->rand_gauss( 162 | 3.84, .09, true); // includes stat unc but not syst 163 | phoTravT += RandomGen::rndm()->rand_gauss( 164 | 0.00, sig, 165 | false); // the overall width added to photon time spectra by the 166 | // effects in the electronics and the data reduction 167 | // pipeline 168 | 169 | if (phoTravT > DBL_MAX) phoTravT = tau_a; 170 | if (phoTravT < -DBL_MAX) phoTravT = 0.000; 171 | 172 | return phoTravT; // this function follows LUX (arXiv:1802.06162) not Xe10 173 | // technically but tried to make general 174 | } 175 | 176 | virtual vector SinglePEWaveForm(double area, double t0) override { 177 | vector PEperBin; 178 | 179 | double threshold = PULSEHEIGHT; // photo-electrons 180 | double sigma = PULSE_WIDTH; // ns 181 | area *= 10. * (1. + threshold); 182 | double amplitude = area / (sigma * sqrt(2. * M_PI)), 183 | signal; // assumes perfect Gaussian 184 | 185 | double tStep1 = SAMPLE_SIZE / 1e2; // ns, make sure much smaller than 186 | // sample size; used to generate MC-true 187 | // pulses essentially 188 | double tStep2 = 189 | SAMPLE_SIZE; // ns; 1 over digitization rate, 100 MHz assumed here 190 | 191 | double time = -5. * sigma; 192 | bool digitizeMe = false; 193 | while (true) { 194 | signal = amplitude * exp(-pow(time, 2.) / (2. * sigma * sigma)); 195 | if (signal < threshold) { 196 | if (digitizeMe) 197 | break; 198 | else 199 | ; // do nothing - goes down to advancing time block 200 | } else { 201 | if (digitizeMe) 202 | PEperBin.push_back(signal); 203 | else { 204 | if (RandomGen::rndm()->rand_uniform() < 2. * (tStep1 / tStep2)) { 205 | PEperBin.push_back(time + t0); 206 | PEperBin.push_back(signal); 207 | digitizeMe = true; 208 | } else { 209 | } 210 | } 211 | } 212 | if (digitizeMe) 213 | time += tStep2; 214 | else 215 | time += tStep1; 216 | if (time > 5. * sigma) break; 217 | } 218 | 219 | return PEperBin; 220 | } 221 | // Vary VDetector parameters through custom functions 222 | virtual void ExampleFunction() { set_g1(0.06); } 223 | }; 224 | 225 | #endif 226 | --------------------------------------------------------------------------------