Commit b5ec6358 authored by Håkan Wennlöf's avatar Håkan Wennlöf Committed by Simon Spannagel
Browse files

Added collection form implant as a possible choice, to match more modern APSQ

(cherry picked from commit 5f321ab7)
parent f8f9fed8
Loading
Loading
Loading
Loading
+44 −8
Original line number Diff line number Diff line
@@ -41,10 +41,13 @@ CapacitiveTransferModule::CapacitiveTransferModule(Configuration& config,
    config_.setDefault("cross_coupling", true);
    config_.setDefault("nominal_gap", 0.0);
    config_.setDefault("max_depth_distance", Units::get<double>(5, "um"));
    // By default, collect from the full sensor surface, not the implant region
    config_.setDefault("collect_from_implant", false);
    config_.setDefault("minimum_gap", config_.get<double>("nominal_gap"));

    cross_coupling_ = config_.get<bool>("cross_coupling");
    max_depth_distance_ = config_.get<double>("max_depth_distance");
    collect_from_implant_ = config_.get<bool>("collect_from_implant");

    // Require propagated deposits for single detector
    messenger_->bindSingle<PropagatedChargeMessage>(this, MsgFlags::REQUIRED);
@@ -52,7 +55,23 @@ CapacitiveTransferModule::CapacitiveTransferModule(Configuration& config,

void CapacitiveTransferModule::initialize() {
    
    LOG(DEBUG) << "Max depth distance: " << max_depth_distance_;
    auto model = detector_->getModel();
    if(collect_from_implant_) {
        if(model->getImplants().empty()) {
            throw InvalidValueError(config_,
                                    "collect_from_implant",
                                    "Detector model does not have implants defined, but collection requested from implants");
        }
        if(detector_->getElectricFieldType() == FieldType::LINEAR) {
            throw ModuleError("Charge collection from implant region should not be used with linear electric fields.");
        }
        LOG(INFO) << "Collecting charges from implants";
    } else if(!model->getImplants().empty()) {
        LOG(WARNING) << "Detector " << detector_->getName() << " of type " << model->getType()
                     << " has implants defined but collecting charge carriers from full sensor surface, with a max depth distance of " << max_depth_distance_;
    } else {
        LOG(DEBUG) << "Collecting from sensor surface with a max depth distance of " << max_depth_distance_;
    }

    if(config_.count({"coupling_matrix", "coupling_file", "coupling_scan_file"}) > 1) {
        throw InvalidCombinationError(
@@ -271,11 +290,28 @@ void CapacitiveTransferModule::run(Event* event) {
    std::map<Pixel::Index, std::pair<double, std::vector<const PropagatedCharge*>>> pixel_map;
    for(const auto& propagated_charge : propagated_message->getData()) {
        auto position = propagated_charge.getLocalPosition();
        // Ignore if outside depth range of implant
        if(std::fabs(position.z() - (model_->getSensorCenter().z() + model_->getSensorSize().z() / 2.0)) >
        
        if(collect_from_implant_) {
            // Ignore if outside the implant region:
            auto implant = model_->isWithinImplant(position);
            if(!implant.has_value()) {
                LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
                           << Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
                           << " because their local position is outside the pixel implant";
                continue;
            }
            if(implant->getType() != DetectorModel::Implant::Type::FRONTSIDE) {
                LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
                           << Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
                           << " because the pixel implant is located at " << allpix::to_string(implant->getType());
                continue;
            }
        } else if(std::fabs(position.z() - (model_->getSensorCenter().z() + model_->getSensorSize().z() / 2.0)) >
                  max_depth_distance_) {
            LOG(DEBUG) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
                       << propagated_charge.getLocalPosition() << " because their local position is not in implant range";
            // Ignore if not close to the sensor surface:
            LOG(TRACE) << "Skipping set of " << propagated_charge.getCharge() << " propagated charges at "
                       << Units::display(propagated_charge.getLocalPosition(), {"mm", "um"})
                       << " because their local position is not near sensor surface";
            continue;
        }

@@ -338,7 +374,7 @@ void CapacitiveTransferModule::run(Event* event) {
                           << col << "," << row << " pixel " << pixel_index << "with cross-coupling of " << ccpd_factor * 100
                           << "%";

                // Add the pixel the list of hit pixels
                // Add the pixel to the list of hit pixels
                pixel_map[pixel_index].first += neighbour_charge;
                pixel_map[pixel_index].second.emplace_back(&propagated_charge);
            }
+1 −0
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ namespace allpix {

        double normalization_{};
        double max_depth_distance_{};
        bool collect_from_implant_{};
        bool cross_coupling_{};

        void getCapacitanceScan(TFile* root_file);
+2 −1
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ This module requires an installation of Eigen3.
* `cross_coupling`: Enables cross-coupling between pixels. Defaults to `true` (enabled).
* `coupling_file`: Path to the file containing the cross-coupling matrix. The file must contain the relative capacitance to the central pixel.
* `coupling_matrix`: Cross-coupling matrix with relative capacitances.
* `max_depth_distance`: Maximum distance in depth, i.e. normal to the sensor surface at the implant side, for a propagated charge to be taken into account. Defaults to 5um.
* `max_depth_distance`: Maximum distance in depth, i.e. normal to the sensor surface at the implant side, for a propagated charge to be taken into account in case the detector has no implants defined or `collect_from_implant` is set to `false`. Defaults to `5um`.
* `collect_from_implant`: Only consider charge carriers within the implant region of the respective detector instead of the full surface of the sensor. Should only be used with non-linear electric fields and defaults to `false`.
* `output_plots`: Saves the output plots for this module. Defaults to 1 (enabled).

The cross-coupling matrix, to be parsed via the matrix file or via the configuration file, must be organized in Row vs Col, such as: