Detectorphysics Simulation
This notebook will demonstrate the detector physics simulation as well as the simulation of PMT response and DAQ effects. This part of the XENONnT simulation chain was formerly done using the WFSim software.
Imports & Simulation Context
Just like in the previous examples, the Getting_Started and Microphysics_Simulation notebooks, we start by importing the necessary modules and setting up the simulation context.
[32]:
import fuse
[ ]:
st = fuse.context.xenonnt_fuse_full_chain_simulation(
output_folder="./fuse_data",
simulation_config="sr0_dev",
)
st.set_config(
{
"path": "/project2/lgrandi/xenonnt/simulations/testing",
"file_name": "pmt_neutrons_100.root",
"entry_stop": 10,
}
)
run_number = "00000"
INFO:fuse.context:Using corrections run id: 026000
INFO:fuse.context:Using clustering method: dbscan
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/strax/context.py:380: UserWarning: Provides of multi-output plugins overlap, deregister old plugins <class 'straxen.plugins.raw_records.daqreader.DAQReader'>.
warnings.warn(
WARNING:fuse.context:Running without proper corrections! Please provide a corrections_version to ensure proper corrections. Example: 'global_v16'
WARNING:fuse.context:elife not in context config, skipping...
WARNING:fuse.context:electron_drift_velocity not in context config, skipping...
WARNING:fuse.context:electron_drift_time_gate not in context config, skipping...
INFO:fuse.context:Found 'mc_overrides' in config, using override-based config system.
WARNING:strax:Option gain_model_nv purged from context config as it is not used.
WARNING:strax:Option gain_model_mv purged from context config as it is not used.
Running the Simulation
First let us start by running the microphysics simulation up to microphysics_summary. If you have run the Microphysics_Simulation notebook, the data should already be prepared and ready for use.
[9]:
st.make(run_number, "microphysics_summary")
S1 Simulation
The Detectorphysics simulation of fuse splits into two branches, the simulation of S1 signals and the simulation of S2 signals. Lets start first to take a look at the S1 simulation. Two plugins are needed for this: S1PhotonHits and S1PhotonPropagation.
The S1PhotonHits plugin simulates the number of detected photons for each interaction site in the TPC. It takes as input the number of photons at the interaction site that we simulated in the microphysics simulation.
After we have the number of detected photons simulated, we can distribute these photons to the PMTs using the S1PhotonPropagation plugin. Additionally the timing of the photons is simulated here. As output we will get a long list of photons that we can load with the target propagated_s1_photons.
[10]:
st.make(run_number, "s1_photon_hits")
st.make(run_number, "propagated_s1_photons")
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/straxen/storage/mongo_storage.py:321: DownloadWarning: Downloading XENONnT_s1_xyz_patterns_LCE_MCvf051911_wires.pkl to ./resource_cache/02ed2634596793f6e7aa13783745089a
warn(f"Downloading {config_name} to {destination_path}", DownloadWarning)
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/straxen/storage/mongo_storage.py:321: DownloadWarning: Downloading XENONnT_SR0_spe_distributions_20210713_no_noise_scaled.csv to ./resource_cache/99c2cbc580cfa8eeebe831456076e136
warn(f"Downloading {config_name} to {destination_path}", DownloadWarning)
*** Detector definition message ***
You are currently using the default XENON10 template detector.
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/straxen/storage/mongo_storage.py:321: DownloadWarning: Downloading XENONnT_s1_proponly_pc_reflection_optPhot_perPMT_S1_local_20220510.json.gz to ./resource_cache/91f4a6162d5e335c4416165349222061
warn(f"Downloading {config_name} to {destination_path}", DownloadWarning)
[11]:
propagated_s1_photons = st.get_df(run_number, "propagated_s1_photons")
[12]:
propagated_s1_photons.head()
[12]:
| channel | dpe | photon_gain | cluster_id | photon_type | time | endtime | |
|---|---|---|---|---|---|---|---|
| 0 | 32 | False | 1550230 | 7 | 1 | 1315279369 | 1315279369 |
| 1 | 9 | False | 1356800 | 7 | 1 | 1315279370 | 1315279370 |
| 2 | 129 | False | 1756020 | 35 | 1 | 1322863414 | 1322863414 |
| 3 | 67 | False | 2031985 | 33 | 1 | 1322863415 | 1322863415 |
| 4 | 104 | False | 1311268 | 30 | 1 | 1322863416 | 1322863416 |
S2 Simulation
Before we can distribute the photons of the S2 signals to the PMTs like we just did for the S1 signal, we need to simulate what is happening to the electrons at the interaction site. The processes are split into 5 plugins.
Electron Drift and Extraction
First we will simulate the drift of the electrons to the liquid-gas interface. The ElectronDrift plugin calculates how many electrons reach the interface taking into account the electron lifetime and the charge insensitive volume of the detector. Next, we simulate each individual electron’s time and position in the ElectronPropagation plugin. The plugin takes into account diffusion and the drift field in the TPC.
Next the electrons extracted into the gas phase are simulated using the ElectronExtraction plugin. You can load the data of these plugins now if you like, but we will first move to the next step and then combine the results afterwards.
[31]:
st.make(run_number, "drifted_electrons") # data_kind: "interactions_in_roi"
st.make(run_number, "electrons_at_interface") # data_kind: "individual_electrons"
st.make(run_number, "extracted_electrons") # data_kind: "individual_electrons"
Secondary Scintillation
Now that we know how many electrons are extracted into the gas phase, we can simulate the secondary scintillation. This is done using the SecondaryScintillation plugin. The plugin provides two outputs, the number of photons produced by each electron and the number of photons per interaction site (summing over the electrons originating from the interaction).
[15]:
st.make(run_number, "s2_photons")
st.make(run_number, "s2_photons_sum")
S2 Photon Propagation
Just like for the S1 simulation we can now distribute the S2 photons to the PMTs and calculate theirs arrival times. This is done using the S2PhotonPropagation plugin. The plugin provides similar output as the S1PhotonPropagation plugin, a long list of photons that we can access with the target propagated_s2_photons. As we are dealing with a lot of photons, expecially for interactions with higher energies, the S2 photon propagtion takes a little longer to run than the previous
plugins.
[18]:
st.make(run_number, "propagated_s2_photons")
Combining the Results
Now that we have everything prepared for our S1 and S2 signals we can take a look at how we can combine these simulation results. First we can load s2_photons_sum along with s1_photon_hits, drifted_electrons, extracted_electrons and microphysics_summary.
[20]:
combined_simulation_results = st.get_df(
run_number,
[
"microphysics_summary",
"s2_photons_sum",
"s1_photon_hits",
],
)
Lets take a look at some of the results we just simulated. Here you can see the evolution of the numbers through the simulation chain. Additionaly as all of these plugins have matching data types, this will be the base for the implementation of fastsim into fuse.
[22]:
combined_simulation_results[
[
"n_s1_photon_hits",
"sum_s2_photons",
"photons",
"electrons",
"ed",
]
].head()
[22]:
| n_s1_photon_hits | sum_s2_photons | photons | electrons | ed | |
|---|---|---|---|---|---|
| 0 | 2 | 293 | 34 | 31 | 5.101432 |
| 1 | 449 | 11676 | 4409 | 928 | 74.346542 |
| 2 | 367 | 7634 | 3481 | 652 | 55.221275 |
| 3 | 338 | 8089 | 3156 | 661 | 52.827538 |
| 4 | 423 | 8585 | 3791 | 742 | 61.517601 |
PMT Afterpulses
Now that we know at which PMTs photons the photons are arriving at we can simulate the PMT afterpulses. The PMTAfterPulses plugin provides a list of ‘pseudo’ photons that represent the afterpulse. This way the output of PMTAfterPulses, S1PhotonPropagation and S2PhotonPropagation can be combined and passed to the next simulation steps. The combined output can be loaded using the PhotonSummary plugin.
[23]:
st.make(run_number, "pmt_afterpulses")
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/straxen/storage/mongo_storage.py:321: DownloadWarning: Downloading XENONnT_pmt_afterpulse_config_018435.json.gz to ./resource_cache/a38b18cd61ca67a065a3a32d9b57c2b2
warn(f"Downloading {config_name} to {destination_path}", DownloadWarning)
[24]:
photon_summary = st.get_df(run_number, "photon_summary")
[25]:
photon_summary.head()
[25]:
| channel | dpe | photon_gain | cluster_id | photon_type | time | endtime | |
|---|---|---|---|---|---|---|---|
| 0 | 32 | False | 1550230 | 7 | 1 | 1315279369 | 1315279369 |
| 1 | 9 | False | 1356800 | 7 | 1 | 1315279370 | 1315279370 |
| 2 | 9 | False | 1932976 | 7 | 2 | 1315298052 | 1315298052 |
| 3 | 19 | False | 1888970 | 7 | 2 | 1315298066 | 1315298066 |
| 4 | 399 | False | -577875 | 7 | 2 | 1315298288 | 1315298288 |
PMT Response and DAQ Simulation
Now that we have simulated the propagation of the photons to the PMTs, we can simulate the PMT response and DAQ effects. First we use the PulseWindow plugin to calculate the time intervals (calles pulse_windows) in which the PMT signals can potentially overlap since the photons arrive with time differences shorter than the length of a single photons waveform. Additionaly each photon gets an id that corresponds to the pulse window it belongs to.
[26]:
st.make(run_number, "pulse_windows")
st.make(run_number, "pulse_ids")
Finally we can make raw_records using the PMTResponseAndDAQ plugin. The plugins iterates over all pulse_windows and adds the PMT response of all photons in the pulse. Then the pulse is is split into fragments. These fragments are the final output of fuse: raw_records.
[27]:
st.make(run_number, "raw_records")
/opt/XENONnT/anaconda/envs/XENONnT_el7.sr1_wimp_unblind/lib/python3.9/site-packages/straxen/storage/mongo_storage.py:321: DownloadWarning: Downloading XENONnT_noise_tpc_only_2ms_25118.npz to ./resource_cache/e560759e28f74d5c049425c25e5ae0d5
warn(f"Downloading {config_name} to {destination_path}", DownloadWarning)
[ ]:
[ ]: