src/modules/InducedTransfer/InducedTransferModule.cpp

Implementation of InducedTransfer module. More…

Detailed Description

Implementation of InducedTransfer module.

Copyright: Copyright (c) 2019-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT

Source code


#include "InducedTransferModule.hpp"

#include <string>
#include <utility>

#include "core/module/Event.hpp"
#include "core/utils/log.h"
#include "objects/PixelCharge.hpp"

using namespace allpix;
using namespace ROOT::Math;

InducedTransferModule::InducedTransferModule(Configuration& config, Messenger* messenger, std::shared_ptr<Detector> detector)
    : Module(config, detector), messenger_(messenger), detector_(std::move(detector)) {
    // Enable multithreading of this module if multithreading is enabled
    allow_multithreading();

    // Save detector model
    model_ = detector_->getModel();

    // Set default value for config variables and store value
    config_.setDefault<unsigned int>("distance", 1);
    distance_ = config_.get<unsigned int>("distance");

    // Require propagated deposits for single detector
    messenger_->bindSingle<PropagatedChargeMessage>(this, MsgFlags::REQUIRED);
}

void InducedTransferModule::initialize() {

    // This module requires a weighting potential - otherwise everything is lost...
    if(!detector_->hasWeightingPotential()) {
        throw ModuleError("This module requires a weighting potential.");
    }
}

void InducedTransferModule::run(Event* event) {
    auto propagated_message = messenger_->fetchMessage<PropagatedChargeMessage>(this, event);

    // Calculate induced charge by total motion of charge carriers
    LOG(TRACE) << "Calculating induced charge on pixels";
    bool found_electrons = false, found_holes = false;

    std::map<Pixel::Index, std::vector<std::pair<double, const PropagatedCharge*>>> pixel_map;
    for(const auto& propagated_charge : propagated_message->getData()) {

        // Make sure we're not double-counting by adding induced current information to an existing pulse:
        if(!propagated_charge.getPulses().empty()) {
            throw ModuleError(
                "Received pulse information - this module should not be used with transient information available");
        }

        // Make sure both electrons and holes are present in the input data
        if(propagated_charge.getType() == CarrierType::ELECTRON) {
            found_electrons = true;
        } else if(propagated_charge.getType() == CarrierType::HOLE) {
            found_holes = true;
        }

        const auto* deposited_charge = propagated_charge.getDepositedCharge();

        // Get start and end point by looking at deposited and propagated charge local positions
        auto position_end = propagated_charge.getLocalPosition();
        auto position_start = deposited_charge->getLocalPosition();

        // Find the nearest pixel
        auto [xpixel, ypixel] = model_->getPixelIndex(position_end);

        LOG(TRACE) << "Calculating induced charge from carriers below pixel " << Pixel::Index(xpixel, ypixel)
                   << ", moved from " << Units::display(position_start, {"um", "mm"}) << " to "
                   << Units::display(position_end, {"um", "mm"}) << ", "
                   << Units::display(propagated_charge.getGlobalTime() - deposited_charge->getGlobalTime(), "ns");

        // Loop over NxN pixels:
        auto idx = Pixel::Index(xpixel, ypixel);
        for(const auto& pixel_index : model_->getNeighbors(idx, distance_)) {
            auto ramo_end = detector_->getWeightingPotential(position_end, pixel_index);
            auto ramo_start = detector_->getWeightingPotential(position_start, pixel_index);

            // Induced charge on electrode is q_int = q * (phi(x1) - phi(x0))
            auto induced =
                static_cast<double>(propagated_charge.getSign() * propagated_charge.getCharge()) * (ramo_end - ramo_start);
            LOG(TRACE) << "Pixel " << pixel_index << " dPhi = " << (ramo_end - ramo_start) << ", induced "
                       << propagated_charge.getType() << " q = " << Units::display(induced, "e");

            // Add the pixel the list of hit pixels
            pixel_map[pixel_index].emplace_back(induced, &propagated_charge);
        }
    }

    // Send an error message if this even only contained one of the two carrier types
    if(!found_electrons || !found_holes) {
        LOG_ONCE(ERROR) << "Did not find charge carriers of type \"" << (found_electrons ? "holes" : "electrons")
                        << "\" in this event." << std::endl
                        << "This will cause wrong calculation of induced charge";
    }

    // Create pixel charges
    LOG(TRACE) << "Combining charges at same pixel";
    std::vector<PixelCharge> pixel_charges;
    for(auto& pixel_index_charge : pixel_map) {
        double charge = 0;
        std::vector<const PropagatedCharge*> prop_charges;
        for(auto& prop_pair : pixel_index_charge.second) {
            charge += prop_pair.first;
            prop_charges.push_back(prop_pair.second);
        }

        // Get pixel object from detector
        auto pixel = detector_->getPixel(pixel_index_charge.first.x(), pixel_index_charge.first.y());

        pixel_charges.emplace_back(pixel, std::round(charge), prop_charges);
        LOG(DEBUG) << "Set of " << charge << " charges combined at " << pixel.getIndex();
    }

    // Dispatch message of pixel charges
    auto pixel_message = std::make_shared<PixelChargeMessage>(pixel_charges, detector_);
    messenger_->dispatchMessage(this, pixel_message, event);
}

Updated on 2024-12-13 at 08:31:37 +0000