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

Merge branch 'custom_mobility' into 'master'

Allow Specification of Custom Mobility Functions

See merge request allpix-squared/allpix-squared!620
parents 3e8fb58d 85d156ef
Loading
Loading
Loading
Loading
+39 −0
Original line number Diff line number Diff line
@@ -223,6 +223,45 @@ where $\mu_{m}(N)$ is the Masetti mobility from Equation~\eqref{eq:mob:mas}, and

This model can be selected in the configuration file via the parameter \parameter{mobility_model = "masetti_canali"}.

\subsection{Custom Mobility Models}
\apsq provides the possibility to use fully custom mobility models.
In order to use a custom model, the parameter \parameter{mobility_model = "custom"} needs to be set in the configuration file.
Additionally, the following configuration keys have to be provided:
\begin{description}
    \item[\parameter{mobility_function_electrons}] the formula describing the electron mobility
    \item[\parameter{mobility_function_holes}] the formula describing the hole mobility
\end{description}

The functions defined via these parameters can depend on the local electric field and the local doping concentration.
In order to use the electric field magnitude in the formula, an \parameter{x} has to be placed at the respective position, for the doping concentration a \parameter{y} is used as placeholder.

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

\begin{warning}
Parameters directly placed in the mobility formula have to be supplied in framework-internal units since the function will be evaluated with both electric field strength and 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.
\end{warning}

The following set of parameters re-implements the mobility model presented in Section~\ref{sec:mob:jac} using a custom mobility model.
The mobility is calculated at a fixed temperature of \SI{293}{K}.
\begin{minted}[frame=single,framesep=3pt,breaklines=true,tabsize=2,linenos]{ini}
# Replicating the Jacoboni-Canali mobility model at T = 293K
mobility_model = "custom"

mobility_function_electrons = "[0]/[1]/pow(1.0+pow(x/[1],[2]),1.0/[2])"
mobility_parameters_electrons = 1.0927393e7cm/s, 6729.24V/cm, 1.0916

mobility_function_holes = "[0]/[1]/pow(1.0+pow(x/[1],[2]),1.0/[2])"
mobility_parameters_holes = 8.447804e6cm/s, 17288.57V/cm, 1.2081
\end{minted}

\begin{warning}
It should be noted that the temperature passed via the module configuration is not evaluated for the custom mobility model, but the model parameters need to be manually adjusted to the required temperature.
\end{warning}

The interpretation of the custom mobility functions is based on the \command{ROOT::TFormula} class~\cite{rootformula} and supports all corresponding features, mathematical expressions and constants.

\section{Charge Carrier Lifetime \& Recombination}
\label{sec:recombination}
+6 −0
Original line number Diff line number Diff line
@@ -175,6 +175,12 @@ month={Aug},
    pages = "81-86",
    howpublished = {http://root.cern.ch/},
}
@online{rootformula,
  author = "The ROOT authors",
  title = "ROOT TFormula Class Reference",
  url = "https://root.cern.ch/doc/master/classTFormula.html",
  year = 2013
}
@online{eigen3,
    author = {Ga\"{e}l Guennebaud and Beno\^{i}t Jacob and others},
    title = {Eigen v3},
+33 −0
Original line number Diff line number Diff line
#DESC tests selection of a custom mobility model
[Allpix]
detectors_file = "detector.conf"
number_of_events = 1
random_seed = 0
multithreading = true
workers = 3

[DepositionPointCharge]
model = "fixed"
source_type = "point"
position = 0,0,0

[GenericPropagation]
temperature = 293K
charge_per_step = 100
log_level = DEBUG
propagate_electrons = true
propagate_holes = true

# Replicating the Jacoboni-Canali mobility model at T = 293K
mobility_model = "custom"

mobility_function_electrons = "[0]/[1]/pow(1.0+pow(x/[1],[2]),1.0/[2])"
mobility_parameters_electrons = 1.0927393e7cm/s, 6729.24V/cm, 1.0916

mobility_function_holes = "[0]/[1]/pow(1.0+pow(x/[1],[2]),1.0/[2])"
mobility_parameters_holes = 8.447804e6cm/s, 17288.57V/cm, 1.2081

#PASS (DEBUG) [I:GenericPropagation:mydetector] Selected mobility model "custom"
#LABEL coverage
#FAIL ERROR
#FAIL FATAL
+1 −5
Original line number Diff line number Diff line
@@ -558,11 +558,7 @@ void GenericPropagationModule::initialize() {
    }

    // Prepare mobility model
    try {
        mobility_ = Mobility(config_.get<std::string>("mobility_model"), temperature_, detector->hasDopingProfile());
    } catch(ModelError& e) {
        throw InvalidValueError(config_, "mobility_model", e.what());
    }
    mobility_ = Mobility(config_, detector->hasDopingProfile());

    // Prepare recombination model
    try {
+5 −4
Original line number Diff line number Diff line
@@ -63,7 +63,8 @@ ProjectionPropagationModule::ProjectionPropagationModule(Configuration& config,
    boltzmann_kT_ = Units::get(8.6173e-5, "eV/K") * temperature;

    // Mobility fixed to Jacoboni:
    mobility_ = Mobility("jacoboni", temperature);
    mobility_ = std::make_unique<JacoboniCanali>(temperature);

    // We need direct access to the critical field values of the model since we have a discrete integration of the formula
    // for the total drift time. Taken from https://doi.org/10.1016/0038-1101(77)90054-5 (section 5.2)
    electron_Ec_ = Units::get(1.01 * std::pow(temperature, 1.55), "V/cm");
@@ -200,7 +201,7 @@ void ProjectionPropagationModule::run(Event* event) {
                if(!diffuse_deposit_) {
                    continue;
                }
                double diffusion_constant = boltzmann_kT_ * mobility_(type, efield_mag, doping);
                double diffusion_constant = boltzmann_kT_ * (*mobility_)(type, efield_mag, doping);
                double diffusion_std_dev = std::sqrt(2. * diffusion_constant * integration_time_);
                LOG(TRACE) << "Diffusion width of this charge carrier is " << Units::display(diffusion_std_dev, "um");

@@ -266,13 +267,13 @@ void ProjectionPropagationModule::run(Event* event) {
                double Ec = (type == CarrierType::ELECTRON ? electron_Ec_ : hole_Ec_);

                return ((log(efield_mag_top) - log(efield_mag)) / slope_efield + std::abs(top_z_ - position.z()) / Ec) /
                       mobility_(type, 0, doping);
                       (*mobility_)(type, 0, doping);
            };
            LOG(TRACE) << "Electric field is " << Units::display(efield_mag, "V/cm");

            // Assume linear electric field over the depleted part of the sensor
            double diffusion_constant =
                boltzmann_kT_ * (mobility_(type, efield_mag, doping) + mobility_(type, efield_mag_top, doping)) / 2.;
                boltzmann_kT_ * ((*mobility_)(type, efield_mag, doping) + (*mobility_)(type, efield_mag_top, doping)) / 2.;

            double drift_time = calc_drift_time();
            double propagation_time = deposit.getLocalTime() + drift_time + diffusion_time;
Loading