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

Merge branch 'f-custom-recombination' into 'master'

Add Possibility to Define Custom Recombination Models

See merge request allpix-squared/allpix-squared!986
parents cfada678 24b5ddce
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -117,6 +117,54 @@ lifetime_hole = 4.5ns

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

## Custom Recombination Models

Allpix Squared provides the possibility to use fully custom recombination models. In order to use a custom model, the parameter
`recombination_model = "custom"` needs to be set in the configuration file. Additionally, the following configuration keys have to
be provided:

- `lifetime_function_electrons`:
  The formula describing the electron lifetime.

- `lifetime_function_holes`:
  The formula describing the hole lifetime.

The functions defined via these parameters can depend on the local doping concentration. In
order to use the doping concentration in the formula, an `x` has to be placed at the respective position.

Parameters of the functions can either be placed directly in the formulas in framework-internal units, or provided separately
as arrays via the `lifetime_parameters_electrons` and `lifetime_parameters_electrons`. Placeholders for parameters in the
formula are denoted with squared brackets and a parameter number, for example `[0]` for the first parameter provided.
Parameters specified separately from the formula can contain units which will be interpreted automatically.

{{% alert title="Warning" color="warning" %}}
Parameters directly placed in the recombination formula have to be supplied in framework-internal units since the function will be
evaluated with the doping concentration in internal units. It is recommended to use the
possibility of separately configuring the parameters and to make use of units to avoid conversion mistakes.
{{% /alert %}}

The following set of parameters re-implements the [Shockley-Read-Hall recombination](#shockley-read-hall-recombination) model using a custom
recombination model. The lifetimes are calculated at a fixed temperature of 293 Kelvin.

```ini
# Replicating the Shockley-Read-Hall model at T = 293K
recombination_model = "custom"

lifetime_function_electrons = "[0]/(1 + x / [1])"
lifetime_parameters_electrons = 1.036e-5s, 1e16/cm/cm/cm

lifetime_function_holes = "[0]/(1 + x / [1])"
lifetime_parameters_holes = 4.144e-4s, 7.1e15/cm/cm/cm
```

{{% alert title="Warning" color="warning" %}}
It should be noted that the temperature passed via the module configuration is not evaluated for the custom recombination model,
but the model parameters need to be manually adjusted to the required temperature.
{{% /alert %}}

The interpretation of the custom recombination functions is based on the `ROOT::TFormula` class \[[@rootformula]\] and supports
all corresponding features, mathematical expressions and constants.


[@shockley-read]: https://doi.org/10.1103/PhysRev.87.835
[@hall]: https://doi.org/10.1103/PhysRev.87.387
+39 −0
Original line number Diff line number Diff line
# SPDX-FileCopyrightText: 2017-2023 CERN and the Allpix Squared authors
# SPDX-License-Identifier: MIT

#DESC tests if custom recombination models work properly
[Allpix]
detectors_file = "detector.conf"
number_of_events = 1
random_seed = 0

[DepositionPointCharge]
model = "fixed"
source_type = "point"
position = 445um 220um 0um
number_of_charges = 2000

[ElectricFieldReader]
model = "linear"
bias_voltage = 100V
depletion_voltage = 150V

[DopingProfileReader]
model = "constant"
doping_concentration = -2134560000000

[GenericPropagation]
log_level = INFO
temperature = 293K
charge_per_step = 1
propagate_electrons = false
propagate_holes = true
recombination_model = "custom"

lifetime_function_electrons = "[0]/(1 + x / [1])"
lifetime_parameters_electrons = 1.036e-5s, 1e16/cm/cm/cm

lifetime_function_holes = "[0]/(1 + x / [1])"
lifetime_parameters_holes = 4.144e-4s, 7.1e15/cm/cm/cm

#PASS (INFO) [I:GenericPropagation:mydetector] Selected recombination model "custom"
+58 −0
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#ifndef ALLPIX_RECOMBINATION_MODELS_H
#define ALLPIX_RECOMBINATION_MODELS_H

#include <TFormula.h>

#include "exceptions.h"

#include "core/config/Configuration.hpp"
@@ -171,6 +173,60 @@ namespace allpix {
        double hole_lifetime_;
    };

    /**
     * @ingroup Models
     * @brief Custom recombination model for charge carriers
     */
    class CustomRecombination : virtual public RecombinationModel {
    public:
        CustomRecombination(const Configuration& config, bool doping) {
            electron_lifetime_ = configure_lifetime(config, CarrierType::ELECTRON, doping);
            hole_lifetime_ = configure_lifetime(config, CarrierType::HOLE, doping);
        };

        bool operator()(const CarrierType& type, double doping, double survival_prob, double timestep) const override {
            return survival_prob < (1 - std::exp(-1. * timestep /
                                                 (type == CarrierType::ELECTRON ? electron_lifetime_->Eval(doping)
                                                                                : hole_lifetime_->Eval(doping))));
        };

    private:
        std::unique_ptr<TFormula> electron_lifetime_;
        std::unique_ptr<TFormula> hole_lifetime_;

        std::unique_ptr<TFormula> configure_lifetime(const Configuration& config, const CarrierType type, bool doping) {
            std::string name = (type == CarrierType::ELECTRON ? "electrons" : "holes");
            auto function = config.get<std::string>("lifetime_function_" + name);
            auto parameters = config.getArray<double>("lifetime_parameters_" + name, {});

            auto lifetime = std::make_unique<TFormula>(("lifetime_" + name).c_str(), function.c_str());

            if(!lifetime->IsValid()) {
                throw InvalidValueError(
                    config, "lifetime_function_" + name, "The provided model is not a valid ROOT::TFormula expression");
            }

            // Check if a doping concentration dependency can be detected by checking for the number of dimensions:
            if(!doping && lifetime->GetNdim() == 1) {
                throw ModelUnsuitable("No doping profile available but doping dependence found");
            }

            // Check if number of parameters match up
            if(static_cast<size_t>(lifetime->GetNpar()) != parameters.size()) {
                throw InvalidValueError(config,
                                        "lifetime_parameters_" + name,
                                        "The number of provided parameters and parameters in the function do not match");
            }

            // Set the parameters
            for(size_t n = 0; n < parameters.size(); ++n) {
                lifetime->SetParameter(static_cast<int>(n), parameters[n]);
            }

            return lifetime;
        };
    };

    /**
     * @brief Wrapper class and factory for recombination models.
     *
@@ -206,6 +262,8 @@ namespace allpix {
                } else if(model == "none") {
                    LOG(INFO) << "No charge carrier recombination model chosen, finite lifetime not simulated";
                    model_ = std::make_unique<None>();
                } else if(model == "custom") {
                    model_ = std::make_unique<CustomRecombination>(config, doping);
                } else {
                    throw InvalidModelError(model);
                }