{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Microphysics Simulation\n", "Now that you learned how to run a simulation with fuse from a geant4 file up to `raw_records` we will take a look the the data that fuse can produce in the intermediate simulation steps focusing on the microphysics simulation. This part of the XENONnT simulation chain was formerly done using the `epix` software.\n", "\n", "## Imports & Simulation Context\n", "Just like in the Getting_Started notebook we will first do our necessary imports and then set up the full chain simulation context. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import fuse" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st = fuse.context.xenonnt_fuse_full_chain_simulation(\n", " output_folder=\"./fuse_data\",\n", " simulation_config=\"sr0_dev\",\n", ")\n", "\n", "st.set_config(\n", " {\n", " \"path\": \"/project2/lgrandi/xenonnt/simulations/testing\",\n", " \"file_name\": \"pmt_neutrons_100.root\",\n", " \"entry_stop\": 10,\n", " }\n", ")\n", "\n", "run_number = \"00000\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Running the Simulation\n", "Just like in the Getting_Started notebook we will run the simulation using the `st.make(run_number, \"target\")` function from strax. This time we will tell fuse explicitly to save the intermediate simulation output. \n", "\n", "### Reading the root file and assigning the cluster index\n", "Before we do some simulation and start to calculate stuff, we need to read in the root file from Geant4 and convert it into a data format that can be handled by strax. This step is done in the `ChunkInput` plugin and the result can be accessed using target `geant4_interactions`. This plugin used uproot to open the root file and then converts it into numpy arrays. Each event is then assigned a time based on the config option `source_rate`. The plugin will cut delayed interaction and divide the data into chunks if necessary. \n", "\n", "In the next simulation step we will give each interaction a cluster index. This is done by the `FindCluster` plugin. First, all interactions are grouped by time and then a DBSCAN algorithm is used to spacially cluster the interactions. The output of this plugin can be accessed using target `cluster_index` and can be loaded along with the `geant4_interactions`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st.make(run_number, \"geant4_interactions\")\n", "st.make(run_number, \"cluster_index\")\n", "\n", "geant4_interactions = st.get_df(run_number, [\"geant4_interactions\", \"cluster_index\"])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets take a look at the data:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "geant4_interactions.head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Clustering and Volume Cuts\n", "\n", "In the next step, all interactions with the same `cluster_ids` are merged. The energy of the interactions is summed up and the position and time is calculated as the weighted average of the positions of the individual interactions. The interaction type of the cluster is determined either by the interaction with the highest energy or by the first interaction in the cluster. The interaction type is later used to choose the correct emmision model. \n", "\n", "Following the clustering, the `VolumeProperties` plugin is used to assign physical properties to different detector regions, like the xenon density and the ability to create S2s. The plugin also assigns a `vol_id` to each interaction based on its volume. The volume definitions are stored in a dictionary in fuse.common. \n", "\n", "After the volume properties have been assigned, the `VolumeSelection` plugin is used to select only interactions in the detector regions of interest. By default, these are the TPC and the region below the cathode. \n", "\n", "The next step is the `SelectionMerger` plugin. This plugin merges all interactions that pass the selection into a new data set, the `interactions_in_roi`. By default, the only selection applied is the volume selection, as defined in the `DefaultSimulation` plugin. However, more complex selections can be defined, for example using the `EnergyCut` plugin to select only events with a certain energy range. The selection logic can be used by registring - as an example - the `LowEnergySimulation` plugin, which inherits from `SelectionMerger` and applies both the volume selection and an energy cut defined in the `EnergyCut` plugin." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st.make(run_number, \"clustered_interactions\")\n", "st.make(run_number, \"volume_properties\")\n", "st.make(run_number, \"interactions_in_roi\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Electric Field and Emission Model\n", "\n", "The aim of this simulation part is to model the scintillation and ionization processes at the interaction site. First we need to estimate the electric field strength at the interaction position. This is done in the `ElectricField` plugin using a simulated field map. The field values can be accessed using the target `electric_field_values`. Next we can estimate the number of produced electrons and photons using an emission model. The default implementation of fuse uses the `NestYields` plugin where `nestpy` is used. fuse also provides alternative plugins where the yields are calculated using BBF or a beta response model. These plugins should only be used if you know what you are doing. The result of the emission model can be accessed using the target `quanta`. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st.make(run_number, \"electric_field_values\")\n", "st.make(run_number, \"quanta\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally we can collect the simulation results of the last few steps using the `MicroPhysicsSummary` plugin. This plugin is a `strax.MergeOnlyPlugin` and does not do any calculations. It just merges the results of the previous plugins and can be accessed using the target `microphysics_summary`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "st.make(run_number, \"microphysics_summary\")\n", "\n", "microphysics_summary = st.get_df(run_number, [\"microphysics_summary\"])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "microphysics_summary.head()" ] } ], "metadata": { "kernelspec": { "display_name": "Python [conda env:XENONnT_development] *", "language": "python", "name": "conda-env-XENONnT_development-py" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.18" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }