Commit 5db880a1 authored by Paul Schütze's avatar Paul Schütze
Browse files

Merge branch 'conttrap' into 'master'

Trapping: Add Constant Effective Lifetime Trapping & Detrapping

See merge request allpix-squared/allpix-squared!805
parents 1d5744b7 09156b6b
Loading
Loading
Loading
Loading
+64 −10
Original line number Diff line number Diff line
---
# SPDX-FileCopyrightText: 2022 CERN and the Allpix Squared authors
# SPDX-License-Identifier: CC-BY-4.0
title: "Trapping of Charge Carriers"
title: "Trapping and Detrapping of Charge Carriers"
weight: 4
---

Allpix Squared provides the possibility to simulate the trapping of charge carriers as a consequence of radiation induced
Allpix Squared provides the possibility to simulate the trapping and detrapping of charge carriers as a consequence of radiation induced
lattice defects. Several models exist, that quantify the effective lifetime of electrons and holes, respectively, as a
function of the fluence and, partially, the temperature. The fluence needs to be provided to the corresponding propagation
module, and is always interpreted as 1-MeV neutron equivalent fluence \[[@niel]\].

The decision on whether a charge carrier has been trapped during a step during the propagation process is calculated
similarly to the recombination precesses, described in [Section 5.3](./03_lifetime_recombination.md).
similarly to the recombination processes, described in [Section 5.3](./03_lifetime_recombination.md).

It should be noted that the trapping of charge carriers is only one of several effects induced by radiation damage. In Allpix
Squared, these effects are treated independently, i.e. defining the fluence for a propagation module will not affect any
other process than trapping.

Until now, no models for de-trapping of charge carriers have been implemented. In addition, for most modules, the parameters
have been extracted under certain annealing conditions. A dependency on annealing conditions has not been implemented here.
Please refer to the corresponding reference publications for further details.

The trapping probability is calculated as an exponential decay as a function of the simulation timestep as

```math
p_{e, h} = \left(1 - \exp^{1 \frac{\delta t}{\tau_{e, h}}}\right)
```

where $`\delta t`$ is the simulation timestep and $`\tau{e,h}`$ the effective lifetime of electrons and holes, respectively.
At the same time, a total time spent in the trap is calculated if a detrapping model is selected. Here, the time until the
charge carrier is de-trapped is calculated as

```math
\delta t = - \tau_{e.h} \ln{1-p}
```

where $`p`$ is a probability randomly chosen from a uniform distribution between 0 and 1.

## Trapping Models

The following models for trapping of charge carriers can be selected:

## Ljubljana
### Ljubljana

In the Ljubljana (sometimes referred to as *Kramberger*) model \[[@kramberger]\], the trapping time follows the relation

@@ -61,7 +78,7 @@ injection at fluences up to $`\Phi_{eq} = 2\times 10^{14} \ n_{eq}\,\text{cm}^2`

This model can be selected in the configuration file via the parameter `trapping_model = "ljubljana"`.

## Dortmund
### Dortmund

The Dortmund (sometimes referred to as *Krasel*) model \[[@dortmundTrapping]\], describes the effective trapping times as

@@ -87,7 +104,7 @@ of the values for proton irradiation.

This model can be selected in the configuration file via the parameter `trapping_model = "dortmund"`.

## CMS Tracker
### CMS Tracker

This effective trapping model has been developed by the CMS Tracker Group. It follows the results of
\[[@CMSTrackerTrapping]\], with measurements at fluences of up to $`\Phi_{eq} = 3 \times 10^{15} \ n_{eq}\,\text{cm}^2`$, at
@@ -117,7 +134,7 @@ No temperature scaling is provided.

This model can be selected in the configuration file via the parameter `trapping_model = "cmstracker"`.

## Mandic
### Mandic

The Mandić model \[[@Mandic]\] is an empirical model developed from measurements with high fluences ranging from
$`\Phi_{eq} = 5\times 10^{15} \ n_{eq}\,\text{cm}^2`$ to $`\Phi_{eq} = 1\times 10^{17} \ n_{eq}\,\text{cm}^2`$ and describes
@@ -147,7 +164,23 @@ values in Weightfield2 \[[@weightfield2]\].

This model can be selected in the configuration file via the parameter `trapping_model = "mandic"`.

## Custom Trapping Model
### Constant Trapping Model

For some situations or materials, a constant trapping probability is necessary. This can be achieved with the constant
trapping model. Here, the lifetimes are constant and set from the values provided in the configuration file with the
parameters `trapping_time_electron` and `trapping_time_hole`:

```ini
# Constant trapping times for electrons and holes:
trapping_model = "constant"
trapping_time_electron = 5ns
trapping_time_hole = 5ns
```

This model can be selected in the configuration file via the parameter `trapping_model = "constant"`.


### Custom Trapping Model

Similarly to the mobility models described above, Allpix Squared provides the possibility to use fully custom trapping
models. The model requires the following configuration keys:
@@ -199,6 +232,27 @@ trapping_parameters_holes = 7ns

This model can be selected in the configuration file via the parameter `trapping_model = "custom"`.

## Detrapping Models

The detrapping is configured via the `detrapping_model` parameter. Currently, only `detrapping_model = "none"` and
`detrapping_model = "constant"` are supported.

The following models for trapping of charge carriers can be selected:

### Constant Detrapping Model

A constant detrapping probability, with the detrapping time defined separately for electrons and holes, can be implemented via the constant detrapping model.
This model requires the parameters `detrapping_time_electron` and `detrapping_time_hole` to be configured.

```ini
# Constant detrapping times for electrons and holes:
detrapping_model = "constant"
detrapping_time_electron = 10ns
detrapping_time_hole = 10ns
```




[@niel]: https://rd48.web.cern.ch/technical-notes/rosetn972.ps
[@kramberger]: https://doi.org/10.1016/S0168-9002(01)01263-3
+41 −16
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ GenericPropagationModule::GenericPropagationModule(Configuration& config,
    config_.setDefault<std::string>("mobility_model", "jacoboni");
    config_.setDefault<std::string>("recombination_model", "none");
    config_.setDefault<std::string>("trapping_model", "none");
    config_.setDefault<std::string>("detrapping_model", "none");

    config_.setDefault<bool>("output_linegraphs", false);
    config_.setDefault<bool>("output_linegraphs_collected", false);
@@ -203,7 +204,11 @@ void GenericPropagationModule::initialize() {
                                  1);

        trapped_histo_ = CreateHistogram<TH1D>(
            "trapping_histo", "Fraction of trapped charge carriers;trapping [N / N_{total}] ;number of events", 100, 0, 1);
            "trapping_histo",
            "Fraction of trapped charge carriers at final state;trapping [N / N_{total}] ;number of events",
            100,
            0,
            1);

        recombination_time_histo_ =
            CreateHistogram<TH1D>("recombination_time_histo",
@@ -212,7 +217,13 @@ void GenericPropagationModule::initialize() {
                                  0,
                                  static_cast<double>(Units::convert(integration_time_, "ns")));
        trapping_time_histo_ = CreateHistogram<TH1D>("trapping_time_histo",
                                                     "Time until trapping of charge carriers;time [ns];charge carriers",
                                                     "Local time of trapping of charge carriers;time [ns];charge carriers",
                                                     static_cast<int>(Units::convert(integration_time_, "ns") * 5),
                                                     0,
                                                     static_cast<double>(Units::convert(integration_time_, "ns")));
        detrapping_time_histo_ =
            CreateHistogram<TH1D>("detrapping_time_histo",
                                  "Time from trapping until detrapping of charge carriers;time [ns];charge carriers",
                                  static_cast<int>(Units::convert(integration_time_, "ns") * 5),
                                  0,
                                  static_cast<double>(Units::convert(integration_time_, "ns")));
@@ -237,6 +248,9 @@ void GenericPropagationModule::initialize() {

    // Prepare trapping model
    trapping_ = Trapping(config_);

    // Prepare trapping model
    detrapping_ = Detrapping(config_);
}

void GenericPropagationModule::run(Event* event) {
@@ -306,8 +320,12 @@ void GenericPropagationModule::run(Event* event) {
            }

            // Propagate a single charge deposit
            auto [final_position, time, gain, state] = propagate(
                initial_position, deposit.getType(), deposit.getLocalTime(), event->getRandomEngine(), output_plot_points);
            auto [final_position, time, gain, state] = propagate(initial_position,
                                                                 deposit.getType(),
                                                                 deposit.getLocalTime(),
                                                                 event->getRandomEngine(),
                                                                 output_plot_points,
                                                                 charge_per_step);

            if(state == CarrierState::RECOMBINED) {
                LOG(DEBUG) << " Recombined " << charge_per_step << " at " << Units::display(final_position, {"mm", "um"})
@@ -321,9 +339,6 @@ void GenericPropagationModule::run(Event* event) {
                LOG(DEBUG) << " Trapped " << charge_per_step << " at " << Units::display(final_position, {"mm", "um"})
                           << " in " << Units::display(time, "ns") << " time, removing";
                trapped_charges_count += charge_per_step;
                if(output_plots_) {
                    trapping_time_histo_->Fill(static_cast<double>(Units::convert(time, "ns")), charge_per_step);
                }
                continue;
            }

@@ -405,7 +420,8 @@ GenericPropagationModule::propagate(const ROOT::Math::XYZPoint& pos,
                                    const CarrierType& type,
                                    const double initial_time,
                                    RandomNumberGenerator& random_generator,
                                    LineGraph::OutputPlotPoints& output_plot_points) const {
                                    LineGraph::OutputPlotPoints& output_plot_points,
                                    const unsigned int charge) const {
    // Create a runge kutta solver using the electric field as step function
    Eigen::Vector3d position(pos.x(), pos.y(), pos.z());

@@ -426,8 +442,8 @@ GenericPropagationModule::propagate(const ROOT::Math::XYZPoint& pos,
        return diffusion;
    };

    // Survival probability of this charge carrier package, evaluated at every step
    allpix::uniform_real_distribution<double> survival(0, 1);
    // Survival or detrap probability of this charge carrier package, evaluated at every step
    allpix::uniform_real_distribution<double> uniform_distribution(0, 1);

    // Define lambda functions to compute the charge carrier velocity with or without magnetic field
    std::function<Eigen::Vector3d(double, const Eigen::Vector3d&)> carrier_velocity_noB =
@@ -515,18 +531,26 @@ GenericPropagationModule::propagate(const ROOT::Math::XYZPoint& pos,
        // Check if charge carrier is still alive:
        if(recombination_(type,
                          detector_->getDopingConcentration(static_cast<ROOT::Math::XYZPoint>(position)),
                          survival(random_generator),
                          uniform_distribution(random_generator),
                          timestep)) {
            state = CarrierState::RECOMBINED;
        }

        // Check if the charge carrier has been trapped:
        auto [trapped, traptime] = trapping_(type, survival(random_generator), timestep, std::sqrt(efield.Mag2()));
        if(trapped) {
            if((initial_time + runge_kutta.getTime() + traptime) < integration_time_) {
        if(trapping_(type, uniform_distribution(random_generator), timestep, std::sqrt(efield.Mag2()))) {
            if(output_plots_) {
                trapping_time_histo_->Fill(static_cast<double>(Units::convert(runge_kutta.getTime(), "ns")), charge);
            }

            auto detrap_time = detrapping_(type, uniform_distribution(random_generator), std::sqrt(efield.Mag2()));
            if((initial_time + runge_kutta.getTime() + detrap_time) < integration_time_) {
                LOG(DEBUG) << "De-trapping charge carrier after " << Units::display(detrap_time, {"ns", "us"});
                // De-trap and advance in time if still below integration time
                LOG(DEBUG) << "De-trapping charge carrier after " << Units::display(traptime, {"ns", "us"});
                runge_kutta.advanceTime(traptime);
                runge_kutta.advanceTime(detrap_time);

                if(output_plots_) {
                    detrapping_time_histo_->Fill(static_cast<double>(Units::convert(detrap_time, "ns")), charge);
                }
            } else {
                // Mark as trapped otherwise
                state = CarrierState::TRAPPED;
@@ -622,6 +646,7 @@ void GenericPropagationModule::finalize() {
        trapped_histo_->Write();
        recombination_time_histo_->Write();
        trapping_time_histo_->Write();
        detrapping_time_histo_->Write();
        gain_histo_->Write();
    }

+6 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include "objects/DepositedCharge.hpp"
#include "objects/PropagatedCharge.hpp"

#include "physics/Detrapping.hpp"
#include "physics/ImpactIonization.hpp"
#include "physics/Mobility.hpp"
#include "physics/Recombination.hpp"
@@ -84,6 +85,7 @@ namespace allpix {
         * @param initial_time Initial time passed before propagation starts in local time coordinates
         * @param random_generator Reference to the random number engine to be used
         * @param output_plot_points Reference to vector to hold points for line graph output plots
         * @param charge Total charge of the observed charge carrier set
         * @return Tuple with the point where the deposit ended after propagation, the time the propagation took, the
         * cumulative gain and the final state of the charge carrier at the end of processing
         */
@@ -92,7 +94,8 @@ namespace allpix {
                  const CarrierType& type,
                  const double initial_time,
                  RandomNumberGenerator& random_generator,
                  LineGraph::OutputPlotPoints& output_plot_points) const;
                  LineGraph::OutputPlotPoints& output_plot_points,
                  const unsigned int charge) const;

        // Local copies of configuration parameters to avoid costly lookup:
        double temperature_{}, timestep_min_{}, timestep_max_{}, timestep_start_{}, integration_time_{},
@@ -108,6 +111,7 @@ namespace allpix {
        Recombination recombination_;
        ImpactIonization multiplication_;
        Trapping trapping_;
        Detrapping detrapping_;

        // Precalculated value for Boltzmann constant:
        double boltzmann_kT_;
@@ -132,6 +136,7 @@ namespace allpix {
        Histogram<TH1D> trapped_histo_;
        Histogram<TH1D> recombination_time_histo_;
        Histogram<TH1D> trapping_time_histo_;
        Histogram<TH1D> detrapping_time_histo_;
        Histogram<TH1D> gain_histo_;
    };

+5 −0
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@ Some models include temperature-dependent scaling of trapping probabilities, and
The trapping probability is calculated at each step of the propagation by drawing a random number from an uniform distribution with $`0 \leq r \leq 1`$ and comparing it to the expression $`1 - e^{-dt/\tau_{eff}}`$, where $`dt`$ is the time step of the last charge carrier movement and $`\tau_{eff}`$ the effective trapping time constant.
A list of available models can be found in the user manual.

Detrapping of charge carriers can be enabled by setting a detrapping model via the parameter `detrapping_model`.
The default value is `none`, corresponding to no charge carrier detrapping being simulated.
A list of available models can be found in the user manual.

The propagation module also produces a variety of output plots. These include a 3D line plot of the path of all separately propagated charge carrier sets from their point of deposition to the end of their drift, with nearby paths having different colors. In this coloring scheme, electrons are marked in blue colors, while holes are presented in different shades of orange.
In addition, a 3D GIF animation for the drift of all individual sets of charges (with the size of the point proportional to the number of charges in the set) can be produced. Finally, the module produces 2D contour animations in all the planes normal to the X, Y and Z axis, showing the concentration flow in the sensor.
It should be noted that generating the animations is very time-consuming and should be switched off even when investigating drift behavior.
@@ -49,6 +53,7 @@ This module requires an installation of Eigen3.
* `recombination_model`: Charge carrier lifetime model to be used for the propagation. Defaults to `none`, a list of available models can be found in the documentation. This feature requires a doping concentration to be present for the detector.
* `trapping_model`: Model for simulating charge carrier trapping from radiation-induced damage. Defaults to `none`, a list of available models can be found in the documentation. All models require explicitly setting a fluence parameter.
* `fluence`: 1MeV-neutron equivalent fluence the sensor has been exposed to.
* `detrapping_model`: Model for simulating charge carrier detrapping from radiation-induced damage. Defaults to `none`, a list of available models can be found in the documentation.
* `charge_per_step` : Maximum number of charge carriers to propagate together. Divides the total number of deposited charge carriers at a specific point into sets of this number of charge carriers and a set with the remaining charge carriers. A value of 10 charges per step is used by default if this value is not specified.
* `max_charge_groups`: Maximum number of charge groups to propagate from a single deposit point. Temporarily increases the value of `charge_per_step` to reduce the number of propagated groups if the deposit is larger than the value `max_charge_groups`*`charge_per_step`, thus reducing the negative performance impact of unexpectedly large deposits. The default value is 1000 charge groups. If it is set to 0, there is no upper limit on the number of charge groups propagated.
* `spatial_precision` : Spatial precision to aim for. The timestep of the Runge-Kutta propagation is adjusted to reach this spatial precision after calculating the uncertainty from the fifth-order error method. Defaults to 0.25nm.
+1 −1
Original line number Diff line number Diff line
@@ -31,4 +31,4 @@ trapping_parameters_electrons = 10ns
trapping_function_holes = "[0]"
trapping_parameters_holes = 10ns

#PASS Trapped 1385 charges during transport
#PASS Trapped 1365 charges during transport
Loading